@inkbar/core 0.10.0 → 0.11.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/README.md +19 -1
- package/dist/dom.d.mts +146 -1
- package/dist/dom.d.mts.map +1 -1
- package/dist/dom.mjs +655 -36
- package/dist/dom.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
package/dist/dom.mjs
CHANGED
|
@@ -228,6 +228,347 @@ function mostVisiblePageCss(layout, scrollTop, viewportHeight) {
|
|
|
228
228
|
return best;
|
|
229
229
|
}
|
|
230
230
|
//#endregion
|
|
231
|
+
//#region src/dom/pinch.ts
|
|
232
|
+
const midpoint = (a, b) => ({
|
|
233
|
+
x: (a.x + b.x) / 2,
|
|
234
|
+
y: (a.y + b.y) / 2
|
|
235
|
+
});
|
|
236
|
+
function pinchFrame(start, now, startScale, minZoom, maxZoom) {
|
|
237
|
+
const from = Math.hypot(start[1].x - start[0].x, start[1].y - start[0].y);
|
|
238
|
+
const to = Math.hypot(now[1].x - now[0].x, now[1].y - now[0].y);
|
|
239
|
+
const zoom = from > 0 ? Math.min(maxZoom, Math.max(minZoom, startScale * to / from)) : startScale;
|
|
240
|
+
const anchor = midpoint(start[0], start[1]);
|
|
241
|
+
const mid = midpoint(now[0], now[1]);
|
|
242
|
+
return {
|
|
243
|
+
zoom,
|
|
244
|
+
factor: from > 0 ? zoom / startScale : 1,
|
|
245
|
+
anchor,
|
|
246
|
+
pan: {
|
|
247
|
+
x: mid.x - anchor.x,
|
|
248
|
+
y: mid.y - anchor.y
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const SETTLE_MS = 150;
|
|
253
|
+
/**
|
|
254
|
+
* The pinch preview: a transform on the pages element, which the browser composites without
|
|
255
|
+
* laying anything out. `settle` covers what a preview can show but the committed layout
|
|
256
|
+
* cannot reach (mainly scroll clamped at the content edges): the pages start where the fingers
|
|
257
|
+
* left them and slide into place.
|
|
258
|
+
*/
|
|
259
|
+
function createZoomPreview(container, pagesEl) {
|
|
260
|
+
const win = container.ownerDocument.defaultView ?? window;
|
|
261
|
+
let frame = 0;
|
|
262
|
+
let timer;
|
|
263
|
+
function stopSettling() {
|
|
264
|
+
if (frame) win.cancelAnimationFrame(frame);
|
|
265
|
+
frame = 0;
|
|
266
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
267
|
+
timer = void 0;
|
|
268
|
+
pagesEl.removeEventListener("transitionend", onTransitionEnd);
|
|
269
|
+
pagesEl.style.removeProperty("transition");
|
|
270
|
+
}
|
|
271
|
+
function settled() {
|
|
272
|
+
stopSettling();
|
|
273
|
+
pagesEl.style.removeProperty("transform");
|
|
274
|
+
}
|
|
275
|
+
const onTransitionEnd = (e) => {
|
|
276
|
+
if (e.target === pagesEl && e.propertyName === "transform") settled();
|
|
277
|
+
};
|
|
278
|
+
function clear() {
|
|
279
|
+
stopSettling();
|
|
280
|
+
pagesEl.style.removeProperty("transform");
|
|
281
|
+
pagesEl.style.removeProperty("transform-origin");
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
show(factor, anchor, pan) {
|
|
285
|
+
stopSettling();
|
|
286
|
+
const x = anchor.x + container.scrollLeft - pagesEl.offsetLeft;
|
|
287
|
+
const y = anchor.y + container.scrollTop - pagesEl.offsetTop;
|
|
288
|
+
pagesEl.style.transformOrigin = `${x}px ${y}px`;
|
|
289
|
+
pagesEl.style.transform = `translate(${pan.x}px, ${pan.y}px) scale(${factor})`;
|
|
290
|
+
},
|
|
291
|
+
clear,
|
|
292
|
+
settle(offset) {
|
|
293
|
+
clear();
|
|
294
|
+
pagesEl.style.transform = `translate(${offset.x}px, ${offset.y}px)`;
|
|
295
|
+
frame = win.requestAnimationFrame(() => {
|
|
296
|
+
frame = 0;
|
|
297
|
+
getComputedStyle(pagesEl).transform;
|
|
298
|
+
pagesEl.style.transition = `transform ${SETTLE_MS}ms ease-out`;
|
|
299
|
+
pagesEl.style.removeProperty("transform");
|
|
300
|
+
pagesEl.addEventListener("transitionend", onTransitionEnd);
|
|
301
|
+
timer = setTimeout(settled, 200);
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
destroy: clear
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Tracks touches on the container with touch events, which every touch browser sends as one
|
|
309
|
+
* stream (pointer events may be cancelled once the browser decides to pan). The first two
|
|
310
|
+
* fingers down start a pinch; a third is ignored. The pinch ends when either of its fingers
|
|
311
|
+
* lifts or is cancelled, and any finger still down then starts nothing until every finger has
|
|
312
|
+
* lifted. While two fingers are down their moves are prevented, so the browser neither pans
|
|
313
|
+
* nor zooms underneath; one finger's moves never are, so a one-finger scroll stays native. A
|
|
314
|
+
* move the browser will not let us cancel means it is scrolling natively already: that travel
|
|
315
|
+
* is left to it, and only what the fingers do after it is scrolled here.
|
|
316
|
+
* The viewport claims pinch on the container (`touch-action: pan-x pan-y`), which keeps the
|
|
317
|
+
* page from zooming as well; the tool controller turns off one-finger panning on the pages
|
|
318
|
+
* element while a drag tool is ready, and two fingers still land here. A finger
|
|
319
|
+
* that lands alone starts afresh, so an end the browser never sent leaves nothing behind.
|
|
320
|
+
*/
|
|
321
|
+
function attachTouchPinch(opts) {
|
|
322
|
+
const { container, preview } = opts;
|
|
323
|
+
let pinch;
|
|
324
|
+
let spent = false;
|
|
325
|
+
/** The touches that went down on the container, by identifier. */
|
|
326
|
+
const mine = /* @__PURE__ */ new Set();
|
|
327
|
+
const down = (list) => [...list].filter((t) => mine.has(t.identifier));
|
|
328
|
+
function at(t) {
|
|
329
|
+
const r = container.getBoundingClientRect();
|
|
330
|
+
return {
|
|
331
|
+
x: t.clientX - r.left - container.clientLeft,
|
|
332
|
+
y: t.clientY - r.top - container.clientTop
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const onStart = (e) => {
|
|
336
|
+
const live = new Set([...e.touches].map((t) => t.identifier));
|
|
337
|
+
for (const id of mine) if (!live.has(id)) mine.delete(id);
|
|
338
|
+
for (const t of e.changedTouches) mine.add(t.identifier);
|
|
339
|
+
const fingers = down(e.touches);
|
|
340
|
+
if (fingers.length === 1) {
|
|
341
|
+
if (pinch) preview.clear();
|
|
342
|
+
pinch = void 0;
|
|
343
|
+
spent = false;
|
|
344
|
+
}
|
|
345
|
+
if (pinch || spent || fingers.length < 2) return;
|
|
346
|
+
const [a, b] = fingers;
|
|
347
|
+
const start = [at(a), at(b)];
|
|
348
|
+
const scale = opts.scale();
|
|
349
|
+
pinch = {
|
|
350
|
+
ids: [a.identifier, b.identifier],
|
|
351
|
+
start,
|
|
352
|
+
scale,
|
|
353
|
+
last: pinchFrame(start, start, scale, opts.minZoom, opts.maxZoom),
|
|
354
|
+
scrolled: {
|
|
355
|
+
x: 0,
|
|
356
|
+
y: 0
|
|
357
|
+
},
|
|
358
|
+
mid: midpoint(start[0], start[1])
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
const onMove = (e) => {
|
|
362
|
+
if (down(e.touches).length >= 2 && e.cancelable) e.preventDefault();
|
|
363
|
+
if (!pinch) return;
|
|
364
|
+
const ids = pinch.ids;
|
|
365
|
+
const touches = [...e.touches];
|
|
366
|
+
const a = touches.find((t) => t.identifier === ids[0]);
|
|
367
|
+
const b = touches.find((t) => t.identifier === ids[1]);
|
|
368
|
+
if (!a || !b) return;
|
|
369
|
+
const now = [at(a), at(b)];
|
|
370
|
+
const mid = midpoint(now[0], now[1]);
|
|
371
|
+
const native = !e.cancelable;
|
|
372
|
+
if (native) {
|
|
373
|
+
const dx = mid.x - pinch.mid.x;
|
|
374
|
+
const dy = mid.y - pinch.mid.y;
|
|
375
|
+
pinch.start = [{
|
|
376
|
+
x: pinch.start[0].x + dx,
|
|
377
|
+
y: pinch.start[0].y + dy
|
|
378
|
+
}, {
|
|
379
|
+
x: pinch.start[1].x + dx,
|
|
380
|
+
y: pinch.start[1].y + dy
|
|
381
|
+
}];
|
|
382
|
+
}
|
|
383
|
+
pinch.mid = mid;
|
|
384
|
+
const frame = pinchFrame(pinch.start, now, pinch.scale, opts.minZoom, opts.maxZoom);
|
|
385
|
+
const { scrolled } = pinch;
|
|
386
|
+
if (!native) {
|
|
387
|
+
const took = opts.scrollBy({
|
|
388
|
+
x: frame.pan.x - scrolled.x,
|
|
389
|
+
y: frame.pan.y - scrolled.y
|
|
390
|
+
});
|
|
391
|
+
scrolled.x += took.x;
|
|
392
|
+
scrolled.y += took.y;
|
|
393
|
+
}
|
|
394
|
+
pinch.last = {
|
|
395
|
+
...frame,
|
|
396
|
+
anchor: {
|
|
397
|
+
x: frame.anchor.x + scrolled.x,
|
|
398
|
+
y: frame.anchor.y + scrolled.y
|
|
399
|
+
},
|
|
400
|
+
pan: {
|
|
401
|
+
x: frame.pan.x - scrolled.x,
|
|
402
|
+
y: frame.pan.y - scrolled.y
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
preview.show(pinch.last.factor, pinch.last.anchor, pinch.last.pan);
|
|
406
|
+
};
|
|
407
|
+
const onEnd = (e) => {
|
|
408
|
+
const ended = [...e.changedTouches].map((t) => t.identifier);
|
|
409
|
+
for (const id of ended) mine.delete(id);
|
|
410
|
+
if (pinch && pinch.ids.some((id) => ended.includes(id))) {
|
|
411
|
+
const { last } = pinch;
|
|
412
|
+
pinch = void 0;
|
|
413
|
+
spent = true;
|
|
414
|
+
preview.clear();
|
|
415
|
+
opts.commit(last);
|
|
416
|
+
}
|
|
417
|
+
if (down(e.touches).length === 0) spent = false;
|
|
418
|
+
};
|
|
419
|
+
const listen = { passive: false };
|
|
420
|
+
container.addEventListener("touchstart", onStart, listen);
|
|
421
|
+
container.addEventListener("touchmove", onMove, listen);
|
|
422
|
+
container.addEventListener("touchend", onEnd, listen);
|
|
423
|
+
container.addEventListener("touchcancel", onEnd, listen);
|
|
424
|
+
return { destroy() {
|
|
425
|
+
container.removeEventListener("touchstart", onStart);
|
|
426
|
+
container.removeEventListener("touchmove", onMove);
|
|
427
|
+
container.removeEventListener("touchend", onEnd);
|
|
428
|
+
container.removeEventListener("touchcancel", onEnd);
|
|
429
|
+
mine.clear();
|
|
430
|
+
pinch = void 0;
|
|
431
|
+
spent = false;
|
|
432
|
+
} };
|
|
433
|
+
}
|
|
434
|
+
const WHEEL_STEP = 1.1;
|
|
435
|
+
/**
|
|
436
|
+
* The zoom factor one wheel event stands for. A trackpad pinch arrives as ctrl+wheel events
|
|
437
|
+
* with small pixel deltas, which zoom smoothly. A mouse wheel notch with ctrl held arrives in
|
|
438
|
+
* line or page mode, or as one large pixel delta, and is one small step, whatever its size.
|
|
439
|
+
*/
|
|
440
|
+
function wheelFactor(e) {
|
|
441
|
+
if (e.deltaMode !== 0 || Math.abs(e.deltaY) >= 50) return e.deltaY < 0 ? WHEEL_STEP : e.deltaY > 0 ? 1 / WHEEL_STEP : 1;
|
|
442
|
+
return Math.exp(-e.deltaY / 100);
|
|
443
|
+
}
|
|
444
|
+
const IDLE_MS = 150;
|
|
445
|
+
const TOUCH_TYPES = [
|
|
446
|
+
"touchstart",
|
|
447
|
+
"touchend",
|
|
448
|
+
"touchcancel"
|
|
449
|
+
];
|
|
450
|
+
/**
|
|
451
|
+
* A trackpad pinch over the container. Browsers report it as ctrl+wheel events, which are
|
|
452
|
+
* taken over so the page does not zoom; a run of them is one pinch, anchored at the pointer
|
|
453
|
+
* where it began and committed once the events stop, or as soon as the container scrolls (a
|
|
454
|
+
* plain wheel, a scroll bar), so the scroll moves the committed layout and not the preview.
|
|
455
|
+
* Safari also has gesture events, which give the scale directly; while one of those runs,
|
|
456
|
+
* ctrl+wheel is ignored so nothing counts twice. Safari on a touch screen sends gesture events
|
|
457
|
+
* for a touch pinch too, which `attachTouchPinch` already handles: while a finger is down on
|
|
458
|
+
* the container they are prevented and otherwise ignored. The wheel listener is non-passive
|
|
459
|
+
* because it has to call `preventDefault`.
|
|
460
|
+
*/
|
|
461
|
+
function attachTrackpadPinch(opts) {
|
|
462
|
+
const { container, preview } = opts;
|
|
463
|
+
const win = container.ownerDocument.defaultView ?? window;
|
|
464
|
+
let run;
|
|
465
|
+
let gesturing = false;
|
|
466
|
+
/** A finger is down on the container. */
|
|
467
|
+
let touching = false;
|
|
468
|
+
let timer;
|
|
469
|
+
function at(e) {
|
|
470
|
+
const r = container.getBoundingClientRect();
|
|
471
|
+
return {
|
|
472
|
+
x: e.clientX - r.left - container.clientLeft,
|
|
473
|
+
y: e.clientY - r.top - container.clientTop
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
function update(e, next) {
|
|
477
|
+
if (!run) {
|
|
478
|
+
const scale = opts.scale();
|
|
479
|
+
const anchor = at(e);
|
|
480
|
+
run = {
|
|
481
|
+
anchor,
|
|
482
|
+
scale,
|
|
483
|
+
factor: 1,
|
|
484
|
+
last: pinchFrame([anchor, anchor], [anchor, anchor], scale, opts.minZoom, opts.maxZoom)
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
run.factor = next(run.factor);
|
|
488
|
+
const zoom = Math.min(opts.maxZoom, Math.max(opts.minZoom, run.scale * run.factor));
|
|
489
|
+
run.last = {
|
|
490
|
+
zoom,
|
|
491
|
+
factor: zoom / run.scale,
|
|
492
|
+
anchor: run.anchor,
|
|
493
|
+
pan: {
|
|
494
|
+
x: 0,
|
|
495
|
+
y: 0
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
preview.show(run.last.factor, run.last.anchor, run.last.pan);
|
|
499
|
+
}
|
|
500
|
+
function end() {
|
|
501
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
502
|
+
timer = void 0;
|
|
503
|
+
if (!run) return;
|
|
504
|
+
const { last } = run;
|
|
505
|
+
run = void 0;
|
|
506
|
+
preview.clear();
|
|
507
|
+
opts.commit(last);
|
|
508
|
+
}
|
|
509
|
+
const onWheel = (e) => {
|
|
510
|
+
if (!e.ctrlKey) {
|
|
511
|
+
if (!gesturing) end();
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (gesturing) return;
|
|
515
|
+
e.preventDefault();
|
|
516
|
+
update(e, (f) => f * wheelFactor(e));
|
|
517
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
518
|
+
timer = setTimeout(end, IDLE_MS);
|
|
519
|
+
};
|
|
520
|
+
const onScroll = () => {
|
|
521
|
+
if (!gesturing) end();
|
|
522
|
+
};
|
|
523
|
+
const onTouch = (e) => {
|
|
524
|
+
touching = e.type === "touchstart" || e.touches.length > 0;
|
|
525
|
+
};
|
|
526
|
+
const onGestureStart = (e) => {
|
|
527
|
+
e.preventDefault();
|
|
528
|
+
if (touching) return;
|
|
529
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
530
|
+
timer = void 0;
|
|
531
|
+
end();
|
|
532
|
+
gesturing = true;
|
|
533
|
+
update(e, () => 1);
|
|
534
|
+
};
|
|
535
|
+
const onGestureChange = (e) => {
|
|
536
|
+
e.preventDefault();
|
|
537
|
+
if (!gesturing) return;
|
|
538
|
+
update(e, () => e.scale);
|
|
539
|
+
};
|
|
540
|
+
const onGestureEnd = (e) => {
|
|
541
|
+
e.preventDefault();
|
|
542
|
+
if (!gesturing) return;
|
|
543
|
+
gesturing = false;
|
|
544
|
+
end();
|
|
545
|
+
};
|
|
546
|
+
container.addEventListener("wheel", onWheel, { passive: false });
|
|
547
|
+
container.addEventListener("scroll", onScroll, { passive: true });
|
|
548
|
+
const hasGestures = "GestureEvent" in win;
|
|
549
|
+
if (hasGestures) {
|
|
550
|
+
for (const type of TOUCH_TYPES) container.addEventListener(type, onTouch, { passive: true });
|
|
551
|
+
container.addEventListener("gesturestart", onGestureStart);
|
|
552
|
+
container.addEventListener("gesturechange", onGestureChange);
|
|
553
|
+
container.addEventListener("gestureend", onGestureEnd);
|
|
554
|
+
}
|
|
555
|
+
return { destroy() {
|
|
556
|
+
container.removeEventListener("wheel", onWheel);
|
|
557
|
+
container.removeEventListener("scroll", onScroll);
|
|
558
|
+
if (hasGestures) {
|
|
559
|
+
for (const type of TOUCH_TYPES) container.removeEventListener(type, onTouch);
|
|
560
|
+
container.removeEventListener("gesturestart", onGestureStart);
|
|
561
|
+
container.removeEventListener("gesturechange", onGestureChange);
|
|
562
|
+
container.removeEventListener("gestureend", onGestureEnd);
|
|
563
|
+
}
|
|
564
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
565
|
+
timer = void 0;
|
|
566
|
+
run = void 0;
|
|
567
|
+
gesturing = false;
|
|
568
|
+
preview.clear();
|
|
569
|
+
} };
|
|
570
|
+
}
|
|
571
|
+
//#endregion
|
|
231
572
|
//#region src/dom/viewport.ts
|
|
232
573
|
function sameList$1(a, b) {
|
|
233
574
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
@@ -252,10 +593,13 @@ function createViewport(opts) {
|
|
|
252
593
|
});
|
|
253
594
|
const previousStyle = {
|
|
254
595
|
overflow: container.style.overflow,
|
|
255
|
-
position: container.style.position
|
|
596
|
+
position: container.style.position,
|
|
597
|
+
touchAction: container.style.touchAction
|
|
256
598
|
};
|
|
257
599
|
container.dataset.inkViewport = "";
|
|
258
600
|
container.style.overflow = "auto";
|
|
601
|
+
const pinchZoom = opts.pinchZoom ?? true;
|
|
602
|
+
if (pinchZoom) container.style.touchAction = "pan-x pan-y";
|
|
259
603
|
if (getComputedStyle(container).position === "static") container.style.position = "relative";
|
|
260
604
|
const pagesEl = container.ownerDocument.createElement("div");
|
|
261
605
|
pagesEl.className = "ink-pages";
|
|
@@ -422,6 +766,21 @@ function createViewport(opts) {
|
|
|
422
766
|
}
|
|
423
767
|
return best;
|
|
424
768
|
}
|
|
769
|
+
/** The page and user-space point under `anchor` (container-relative), from the current layout. */
|
|
770
|
+
function userAt(anchor) {
|
|
771
|
+
const o = origin();
|
|
772
|
+
const x = container.scrollLeft + anchor.x - o.x;
|
|
773
|
+
const y = container.scrollTop + anchor.y - o.y;
|
|
774
|
+
const box = anchorBox(layout.get(), y);
|
|
775
|
+
if (!box) return void 0;
|
|
776
|
+
return {
|
|
777
|
+
page: box.index,
|
|
778
|
+
user: transformFor(box.index).fromCss({
|
|
779
|
+
x: x - box.left,
|
|
780
|
+
y: y - box.top
|
|
781
|
+
})
|
|
782
|
+
};
|
|
783
|
+
}
|
|
425
784
|
/**
|
|
426
785
|
* Keeps the user-space point under `anchorCss` still while `change` alters the layout.
|
|
427
786
|
* The anchor is taken in layout space, so it holds in the margins and gaps too: there it
|
|
@@ -433,25 +792,103 @@ function createViewport(opts) {
|
|
|
433
792
|
x: container.clientWidth / 2,
|
|
434
793
|
y: container.clientHeight / 2
|
|
435
794
|
};
|
|
436
|
-
const
|
|
437
|
-
const x = container.scrollLeft + anchor.x - o.x;
|
|
438
|
-
const y = container.scrollTop + anchor.y - o.y;
|
|
439
|
-
const old = anchorBox(layout.get(), y);
|
|
440
|
-
const user = old ? transformFor(old.index).fromCss({
|
|
441
|
-
x: x - old.left,
|
|
442
|
-
y: y - old.top
|
|
443
|
-
}) : void 0;
|
|
795
|
+
const at = userAt(anchor);
|
|
444
796
|
change();
|
|
445
797
|
relayout();
|
|
446
|
-
if (!
|
|
447
|
-
const box = boxFor(
|
|
798
|
+
if (!at) return;
|
|
799
|
+
const box = boxFor(at.page);
|
|
448
800
|
if (!box) return;
|
|
449
|
-
const after = transformFor(
|
|
450
|
-
const
|
|
451
|
-
container.scrollLeft =
|
|
452
|
-
container.scrollTop =
|
|
801
|
+
const after = transformFor(at.page).toCss(at.user);
|
|
802
|
+
const o = origin();
|
|
803
|
+
container.scrollLeft = o.x + box.left + after.x - anchor.x;
|
|
804
|
+
container.scrollTop = o.y + box.top + after.y - anchor.y;
|
|
805
|
+
sync();
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* The translation a pinch preview applies, from the current layout. While the zoomed pages
|
|
809
|
+
* would still be narrower than the container, the committed layout centres them (and leaves
|
|
810
|
+
* nothing to scroll sideways), so the preview puts them there instead of following the fingers.
|
|
811
|
+
*/
|
|
812
|
+
function previewPan(factor, anchor, pan) {
|
|
813
|
+
const l = layout.get();
|
|
814
|
+
const widest = l.boxes.reduce((max, b) => Math.max(max, b.width), 0);
|
|
815
|
+
const inner = innerSize().width;
|
|
816
|
+
if (widest * factor >= inner) return pan;
|
|
817
|
+
const o = origin();
|
|
818
|
+
const ox = anchor.x + container.scrollLeft - o.x;
|
|
819
|
+
const shown = o.x - container.scrollLeft + ox + (l.width / 2 - ox) * factor + pan.x;
|
|
820
|
+
const centred = o.x + inner / 2;
|
|
821
|
+
return {
|
|
822
|
+
x: pan.x + centred - shown,
|
|
823
|
+
y: pan.y
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Ends a pinch: one zoom change around the point that started between the fingers, then any
|
|
828
|
+
* travel the touch pinch could not scroll as it went (all of it, for a trackpad pinch) as a
|
|
829
|
+
* scroll, so that point ends up where the preview last showed it.
|
|
830
|
+
* When the scroll's edges stop it getting there, the preview eases the difference out. A
|
|
831
|
+
* pinch that did not zoom changes no zoom, so a fit mode stays: a two-finger pan only
|
|
832
|
+
* scrolls, and a two-finger tap changes nothing.
|
|
833
|
+
*/
|
|
834
|
+
function commitPinch(frame) {
|
|
835
|
+
if (destroyed) return;
|
|
836
|
+
const zoomed = Math.abs(frame.factor - 1) >= .001;
|
|
837
|
+
if (!zoomed && Math.hypot(frame.pan.x, frame.pan.y) < 1) return;
|
|
838
|
+
const at = userAt(frame.anchor);
|
|
839
|
+
const shown = previewPan(frame.factor, frame.anchor, frame.pan);
|
|
840
|
+
if (zoomed) withAnchor(frame.anchor, () => zoom.set(clamp(frame.zoom)));
|
|
841
|
+
container.scrollLeft -= frame.pan.x;
|
|
842
|
+
container.scrollTop -= frame.pan.y;
|
|
453
843
|
sync();
|
|
844
|
+
const box = at && boxFor(at.page);
|
|
845
|
+
if (!at || !box) return;
|
|
846
|
+
const css = transformFor(at.page).toCss(at.user);
|
|
847
|
+
const o = origin();
|
|
848
|
+
const actual = {
|
|
849
|
+
x: o.x + box.left + css.x - container.scrollLeft,
|
|
850
|
+
y: o.y + box.top + css.y - container.scrollTop
|
|
851
|
+
};
|
|
852
|
+
const dx = frame.anchor.x + shown.x - actual.x;
|
|
853
|
+
const dy = frame.anchor.y + shown.y - actual.y;
|
|
854
|
+
if (Math.abs(dx) > 1 || Math.abs(dy) > 1) preview?.settle({
|
|
855
|
+
x: dx,
|
|
856
|
+
y: dy
|
|
857
|
+
});
|
|
454
858
|
}
|
|
859
|
+
const basePreview = pinchZoom ? createZoomPreview(container, pagesEl) : void 0;
|
|
860
|
+
const preview = basePreview && {
|
|
861
|
+
...basePreview,
|
|
862
|
+
show: (factor, anchor, pan) => basePreview.show(factor, anchor, previewPan(factor, anchor, pan))
|
|
863
|
+
};
|
|
864
|
+
const pinch = preview ? attachTouchPinch({
|
|
865
|
+
container,
|
|
866
|
+
preview,
|
|
867
|
+
scale: () => scale.get(),
|
|
868
|
+
minZoom,
|
|
869
|
+
maxZoom,
|
|
870
|
+
scrollBy: (delta) => {
|
|
871
|
+
const { scrollLeft, scrollTop } = container;
|
|
872
|
+
container.scrollTo({
|
|
873
|
+
left: scrollLeft - delta.x,
|
|
874
|
+
top: scrollTop - delta.y,
|
|
875
|
+
behavior: "instant"
|
|
876
|
+
});
|
|
877
|
+
return {
|
|
878
|
+
x: scrollLeft - container.scrollLeft,
|
|
879
|
+
y: scrollTop - container.scrollTop
|
|
880
|
+
};
|
|
881
|
+
},
|
|
882
|
+
commit: commitPinch
|
|
883
|
+
}) : void 0;
|
|
884
|
+
const trackpadPinch = preview ? attachTrackpadPinch({
|
|
885
|
+
container,
|
|
886
|
+
preview,
|
|
887
|
+
scale: () => scale.get(),
|
|
888
|
+
minZoom,
|
|
889
|
+
maxZoom,
|
|
890
|
+
commit: commitPinch
|
|
891
|
+
}) : void 0;
|
|
455
892
|
const viewport = {
|
|
456
893
|
zoom: readonly(zoom),
|
|
457
894
|
scale: readonly(scale),
|
|
@@ -523,6 +960,9 @@ function createViewport(opts) {
|
|
|
523
960
|
destroy() {
|
|
524
961
|
if (destroyed) return;
|
|
525
962
|
destroyed = true;
|
|
963
|
+
pinch?.destroy();
|
|
964
|
+
trackpadPinch?.destroy();
|
|
965
|
+
preview?.destroy();
|
|
526
966
|
ro.disconnect();
|
|
527
967
|
container.removeEventListener("scroll", onScroll);
|
|
528
968
|
if (scrollFrame) cancelAnimationFrame(scrollFrame);
|
|
@@ -536,6 +976,7 @@ function createViewport(opts) {
|
|
|
536
976
|
delete container.dataset.inkViewport;
|
|
537
977
|
container.style.overflow = previousStyle.overflow;
|
|
538
978
|
container.style.position = previousStyle.position;
|
|
979
|
+
container.style.touchAction = previousStyle.touchAction;
|
|
539
980
|
}
|
|
540
981
|
};
|
|
541
982
|
relayout();
|
|
@@ -3744,6 +4185,7 @@ function createPluginHost(opts) {
|
|
|
3744
4185
|
const editFns = [];
|
|
3745
4186
|
const setupCleanups = [];
|
|
3746
4187
|
const redecorators = /* @__PURE__ */ new Set();
|
|
4188
|
+
const pressListeners = /* @__PURE__ */ new Set();
|
|
3747
4189
|
const marks = createMarksRegistry();
|
|
3748
4190
|
const selections = /* @__PURE__ */ new Map();
|
|
3749
4191
|
let opened = null;
|
|
@@ -3784,8 +4226,21 @@ function createPluginHost(opts) {
|
|
|
3784
4226
|
for (const request of requests) request.current.set(null);
|
|
3785
4227
|
for (const cleanup of state.cleanups.reverse()) guard(cleanup);
|
|
3786
4228
|
};
|
|
3787
|
-
const contextFor = (plugin) => {
|
|
4229
|
+
const contextFor = (plugin, owned) => {
|
|
3788
4230
|
const storeNames = /* @__PURE__ */ new Set();
|
|
4231
|
+
const listenForPress = (kind) => (fn) => {
|
|
4232
|
+
const entry = {
|
|
4233
|
+
plugin: plugin.id,
|
|
4234
|
+
kind,
|
|
4235
|
+
fn
|
|
4236
|
+
};
|
|
4237
|
+
pressListeners.add(entry);
|
|
4238
|
+
owned.add(entry);
|
|
4239
|
+
return () => {
|
|
4240
|
+
pressListeners.delete(entry);
|
|
4241
|
+
owned.delete(entry);
|
|
4242
|
+
};
|
|
4243
|
+
};
|
|
3789
4244
|
const ctx = {
|
|
3790
4245
|
id: plugin.id,
|
|
3791
4246
|
document: opts.document,
|
|
@@ -3834,7 +4289,9 @@ function createPluginHost(opts) {
|
|
|
3834
4289
|
scrollTo(page, rect, scrollOpts) {
|
|
3835
4290
|
opts.viewport?.()?.scrollToRect(page, rect, scrollOpts);
|
|
3836
4291
|
},
|
|
3837
|
-
search: opts.search ?? inertSearch
|
|
4292
|
+
search: opts.search ?? inertSearch,
|
|
4293
|
+
onPress: listenForPress("press"),
|
|
4294
|
+
onLongPress: listenForPress("long-press")
|
|
3838
4295
|
};
|
|
3839
4296
|
internals$1.set(ctx, {
|
|
3840
4297
|
marks,
|
|
@@ -3913,13 +4370,22 @@ function createPluginHost(opts) {
|
|
|
3913
4370
|
};
|
|
3914
4371
|
for (const plugin of opts.plugins) {
|
|
3915
4372
|
let cleanup;
|
|
4373
|
+
const owned = /* @__PURE__ */ new Set();
|
|
4374
|
+
const dropPresses = () => {
|
|
4375
|
+
for (const entry of owned) pressListeners.delete(entry);
|
|
4376
|
+
owned.clear();
|
|
4377
|
+
};
|
|
3916
4378
|
try {
|
|
3917
|
-
cleanup = plugin.setup(contextFor(plugin));
|
|
4379
|
+
cleanup = plugin.setup(contextFor(plugin, owned));
|
|
3918
4380
|
} catch (error) {
|
|
4381
|
+
dropPresses();
|
|
3919
4382
|
for (const done of setupCleanups.reverse()) guard(done);
|
|
3920
4383
|
throw error;
|
|
3921
4384
|
}
|
|
3922
|
-
|
|
4385
|
+
setupCleanups.push(() => {
|
|
4386
|
+
dropPresses();
|
|
4387
|
+
if (typeof cleanup === "function") cleanup();
|
|
4388
|
+
});
|
|
3923
4389
|
}
|
|
3924
4390
|
const current = opts.document.get();
|
|
3925
4391
|
if (current) open(current);
|
|
@@ -3968,6 +4434,16 @@ function createPluginHost(opts) {
|
|
|
3968
4434
|
redecorators.delete(run);
|
|
3969
4435
|
};
|
|
3970
4436
|
},
|
|
4437
|
+
emitPress(kind, press) {
|
|
4438
|
+
for (const entry of [...pressListeners]) {
|
|
4439
|
+
if (entry.kind !== kind || !pressListeners.has(entry)) continue;
|
|
4440
|
+
try {
|
|
4441
|
+
entry.fn(press);
|
|
4442
|
+
} catch (error) {
|
|
4443
|
+
console.error(`Plugin ${entry.plugin} threw in a ${kind} listener`, error);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
},
|
|
3971
4447
|
destroy() {
|
|
3972
4448
|
if (destroyed) return;
|
|
3973
4449
|
destroyed = true;
|
|
@@ -5138,6 +5614,48 @@ function createToolOptions(tools = []) {
|
|
|
5138
5614
|
register
|
|
5139
5615
|
};
|
|
5140
5616
|
}
|
|
5617
|
+
const defaultSlop = (pointerType) => pointerType === "mouse" ? 4 : 10;
|
|
5618
|
+
function createPressTracker(opts) {
|
|
5619
|
+
const longPressMs = opts.longPressMs ?? 500;
|
|
5620
|
+
const slopFor = opts.slop ?? defaultSlop;
|
|
5621
|
+
let live;
|
|
5622
|
+
function cancel() {
|
|
5623
|
+
if (live?.timer !== void 0) clearTimeout(live.timer);
|
|
5624
|
+
live = void 0;
|
|
5625
|
+
}
|
|
5626
|
+
return {
|
|
5627
|
+
down(e, press) {
|
|
5628
|
+
cancel();
|
|
5629
|
+
const l = {
|
|
5630
|
+
pointerId: e.pointerId,
|
|
5631
|
+
x: e.clientX,
|
|
5632
|
+
y: e.clientY,
|
|
5633
|
+
slop: slopFor(press.pointerType),
|
|
5634
|
+
press,
|
|
5635
|
+
timer: void 0,
|
|
5636
|
+
done: false
|
|
5637
|
+
};
|
|
5638
|
+
if (press.pointerType !== "mouse") l.timer = setTimeout(() => {
|
|
5639
|
+
l.timer = void 0;
|
|
5640
|
+
l.done = true;
|
|
5641
|
+
opts.onLongPress(press);
|
|
5642
|
+
}, longPressMs);
|
|
5643
|
+
live = l;
|
|
5644
|
+
},
|
|
5645
|
+
move(e) {
|
|
5646
|
+
if (live?.pointerId !== e.pointerId) return;
|
|
5647
|
+
if (Math.hypot(e.clientX - live.x, e.clientY - live.y) > live.slop) cancel();
|
|
5648
|
+
},
|
|
5649
|
+
up(e) {
|
|
5650
|
+
if (live?.pointerId !== e.pointerId) return;
|
|
5651
|
+
const { press, done } = live;
|
|
5652
|
+
cancel();
|
|
5653
|
+
if (!done) opts.onPress(press);
|
|
5654
|
+
},
|
|
5655
|
+
cancel,
|
|
5656
|
+
destroy: cancel
|
|
5657
|
+
};
|
|
5658
|
+
}
|
|
5141
5659
|
//#endregion
|
|
5142
5660
|
//#region src/dom/tools/selection-edit.ts
|
|
5143
5661
|
const CORNERS = [
|
|
@@ -5612,6 +6130,7 @@ function createToolController$1(opts) {
|
|
|
5612
6130
|
clearMade();
|
|
5613
6131
|
made = [...ids];
|
|
5614
6132
|
ctx.root.dataset.inkMade = "";
|
|
6133
|
+
applyClaim();
|
|
5615
6134
|
stopMade = [ctx.store.list.subscribe(checkMade), ctx.store.selection.subscribe(checkMade)];
|
|
5616
6135
|
},
|
|
5617
6136
|
done() {
|
|
@@ -5623,6 +6142,8 @@ function createToolController$1(opts) {
|
|
|
5623
6142
|
const originalCursor = ctx.root.style.cursor;
|
|
5624
6143
|
/** The pointer between a primary press and its release or cancel. */
|
|
5625
6144
|
let pressed;
|
|
6145
|
+
/** Whether `pressed` is a touch, which a second touch turns into a pinch. */
|
|
6146
|
+
let pressedTouch = false;
|
|
5626
6147
|
/** Pointers whose release belongs to a tool that is no longer active. */
|
|
5627
6148
|
const swallow = /* @__PURE__ */ new Set();
|
|
5628
6149
|
/** What the active one-shot tool made; undefined while it is ready. */
|
|
@@ -5630,16 +6151,51 @@ function createToolController$1(opts) {
|
|
|
5630
6151
|
let stopMade = [];
|
|
5631
6152
|
/** Whether the press now under way found the tool made (see the top of the file). */
|
|
5632
6153
|
let pressFoundMade = false;
|
|
6154
|
+
/** Whether the press now under way commits an open editor or closes an open popover. */
|
|
6155
|
+
let pressDismisses = false;
|
|
5633
6156
|
/** Moves and resizes what the active one-shot tool made. */
|
|
5634
6157
|
let madeEdit;
|
|
5635
6158
|
/** The press now under way went to `madeEdit`, not the tool. */
|
|
5636
6159
|
let editingMade = false;
|
|
6160
|
+
const presses = createPressTracker({
|
|
6161
|
+
onPress: (press) => opts.onPress?.("press", press),
|
|
6162
|
+
onLongPress: (press) => opts.onPress?.("long-press", press)
|
|
6163
|
+
});
|
|
5637
6164
|
function clearMade() {
|
|
5638
6165
|
stopMade.forEach((stop) => stop());
|
|
5639
6166
|
stopMade = [];
|
|
5640
6167
|
made = void 0;
|
|
5641
6168
|
delete ctx.root.dataset.inkMade;
|
|
6169
|
+
applyClaim();
|
|
5642
6170
|
}
|
|
6171
|
+
/**
|
|
6172
|
+
* Turns off one-finger panning on the pages element while a claiming tool is ready. The
|
|
6173
|
+
* viewport never sets `touch-action` there, so removing the property restores it. A host
|
|
6174
|
+
* without a pages element (no `createViewport`) gets no claim.
|
|
6175
|
+
*/
|
|
6176
|
+
function applyClaim() {
|
|
6177
|
+
const pages = pagesOf();
|
|
6178
|
+
if (!pages) return;
|
|
6179
|
+
if (claiming()) pages.style.touchAction = "pinch-zoom";
|
|
6180
|
+
else pages.style.removeProperty("touch-action");
|
|
6181
|
+
}
|
|
6182
|
+
function pagesOf() {
|
|
6183
|
+
return ctx.root.querySelector(":scope > [data-ink-pages]");
|
|
6184
|
+
}
|
|
6185
|
+
/** True while the active tool claims touch: it claims it and, if one-shot, is still ready. */
|
|
6186
|
+
function claiming() {
|
|
6187
|
+
return current?.claimsTouch === true && made === void 0;
|
|
6188
|
+
}
|
|
6189
|
+
/**
|
|
6190
|
+
* Safari on iPad can let a one-finger drag scroll the container despite the claim's
|
|
6191
|
+
* `touch-action`, then cancel the tool's gesture. So a claimed one-finger move on the pages
|
|
6192
|
+
* also cancels the browser's scroll. Two fingers are left to the pinch handler or the browser.
|
|
6193
|
+
*/
|
|
6194
|
+
const onTouchMove = (e) => {
|
|
6195
|
+
if (!e.cancelable || e.touches.length !== 1 || !claiming()) return;
|
|
6196
|
+
const pages = pagesOf();
|
|
6197
|
+
if (pages && e.target instanceof Node && pages.contains(e.target)) e.preventDefault();
|
|
6198
|
+
};
|
|
5643
6199
|
/** Leaves the made state when its items are gone, or switches when the selection moved on. */
|
|
5644
6200
|
function checkMade() {
|
|
5645
6201
|
if (!made) return;
|
|
@@ -5653,17 +6209,32 @@ function createToolController$1(opts) {
|
|
|
5653
6209
|
}
|
|
5654
6210
|
/** True for events from a pointer other than the one whose press is live. */
|
|
5655
6211
|
const foreign = (e) => pressed !== void 0 && pressed !== e.pointerId;
|
|
5656
|
-
const readFirst = () => {
|
|
6212
|
+
const readFirst = (e) => {
|
|
5657
6213
|
pressFoundMade = made !== void 0;
|
|
6214
|
+
pressDismisses = dismisses(e.target, ctx.root.ownerDocument);
|
|
5658
6215
|
};
|
|
5659
6216
|
const readAfterEditor = () => {
|
|
5660
6217
|
pressFoundMade ||= made !== void 0;
|
|
5661
6218
|
};
|
|
5662
6219
|
const onDown = (e) => {
|
|
6220
|
+
if (e.pointerType === "touch" && !e.isPrimary) {
|
|
6221
|
+
presses.cancel();
|
|
6222
|
+
if (pressedTouch) cancelPressed();
|
|
6223
|
+
return;
|
|
6224
|
+
}
|
|
6225
|
+
if (e.pointerType === "pen" && pressedTouch && pressed !== void 0) {
|
|
6226
|
+
presses.cancel();
|
|
6227
|
+
cancelPressed();
|
|
6228
|
+
}
|
|
5663
6229
|
if (!e.isPrimary || e.button !== 0 || inCustomContent(e.target)) return;
|
|
5664
6230
|
if (foreign(e)) return;
|
|
5665
6231
|
swallow.delete(e.pointerId);
|
|
5666
|
-
|
|
6232
|
+
pressedTouch = e.pointerType === "touch";
|
|
6233
|
+
const target = e.target instanceof Element ? e.target : null;
|
|
6234
|
+
const annotationId = target?.closest("[data-annotation]")?.getAttribute("data-annotation") ?? void 0;
|
|
6235
|
+
const hit = hitFor(ctx, e);
|
|
6236
|
+
const reportable = (current?.id === "select-text" || current?.id === "select") && annotationId === void 0 && target?.closest("a[href], [data-handle]") == null && !pressDismisses && !closedEditor(e) && hit !== void 0;
|
|
6237
|
+
presses.cancel();
|
|
5667
6238
|
if (current?.kind === "one-shot") {
|
|
5668
6239
|
if (pressFoundMade && annotationId === void 0 && !onScrollBar(e, ctx.root)) {
|
|
5669
6240
|
e.preventDefault();
|
|
@@ -5675,18 +6246,26 @@ function createToolController$1(opts) {
|
|
|
5675
6246
|
if (a && made?.includes(a.id) && madeEdit) {
|
|
5676
6247
|
pressed = e.pointerId;
|
|
5677
6248
|
editingMade = true;
|
|
5678
|
-
if (madeEdit.onPointerDown(e,
|
|
6249
|
+
if (madeEdit.onPointerDown(e, hit, a.id) === true) capture(e.pointerId);
|
|
5679
6250
|
return;
|
|
5680
6251
|
}
|
|
5681
6252
|
if (a && (!isTextMarkup(a) || pressFoundMade)) set("select-text");
|
|
5682
6253
|
}
|
|
5683
6254
|
pressed = e.pointerId;
|
|
5684
|
-
if (current?.onPointerDown?.(e,
|
|
6255
|
+
if (current?.onPointerDown?.(e, hit, annotationId) === true) capture(e.pointerId);
|
|
6256
|
+
if (reportable && pressed === e.pointerId) presses.down(e, {
|
|
6257
|
+
page: hit.page,
|
|
6258
|
+
point: hit.user,
|
|
6259
|
+
pointerType: pointerTypeOf(e)
|
|
6260
|
+
});
|
|
5685
6261
|
};
|
|
5686
6262
|
const onMove = (e) => {
|
|
5687
6263
|
if (!e.isPrimary || foreign(e)) return;
|
|
5688
6264
|
const missed = pressed === e.pointerId && e.buttons === 0;
|
|
5689
|
-
if (missed)
|
|
6265
|
+
if (missed) {
|
|
6266
|
+
pressed = void 0;
|
|
6267
|
+
presses.cancel();
|
|
6268
|
+
} else presses.move(e);
|
|
5690
6269
|
if (editingMade) {
|
|
5691
6270
|
madeEdit?.onPointerMove(e, hitFor(ctx, e));
|
|
5692
6271
|
if (missed) editingMade = false;
|
|
@@ -5706,12 +6285,14 @@ function createToolController$1(opts) {
|
|
|
5706
6285
|
madeEdit?.onPointerUp(e, hitFor(ctx, e));
|
|
5707
6286
|
} else current?.onPointerUp?.(e, hitFor(ctx, e));
|
|
5708
6287
|
release(e.pointerId);
|
|
6288
|
+
presses.up(e);
|
|
5709
6289
|
};
|
|
5710
6290
|
const onCancel = (e) => {
|
|
5711
6291
|
if (!e.isPrimary || foreign(e)) return;
|
|
5712
6292
|
if (e.type === "lostpointercapture" && pressed !== e.pointerId) return;
|
|
5713
6293
|
if (pressed === e.pointerId) pressed = void 0;
|
|
5714
6294
|
swallow.delete(e.pointerId);
|
|
6295
|
+
presses.cancel();
|
|
5715
6296
|
if (editingMade) {
|
|
5716
6297
|
editingMade = false;
|
|
5717
6298
|
madeEdit?.onPointerCancel();
|
|
@@ -5777,6 +6358,7 @@ function createToolController$1(opts) {
|
|
|
5777
6358
|
ctx.root.addEventListener("lostpointercapture", onCancel);
|
|
5778
6359
|
ctx.root.addEventListener("keydown", onKey);
|
|
5779
6360
|
ctx.root.addEventListener("keydown", onEditorEscape, { capture: true });
|
|
6361
|
+
ctx.root.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
5780
6362
|
const loggedUnknown = /* @__PURE__ */ new Set();
|
|
5781
6363
|
function logUnknownOnce(id) {
|
|
5782
6364
|
if (loggedUnknown.has(id)) return;
|
|
@@ -5790,15 +6372,8 @@ function createToolController$1(opts) {
|
|
|
5790
6372
|
return;
|
|
5791
6373
|
}
|
|
5792
6374
|
if (options) registry.merge(id, options);
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
else current?.onPointerCancel?.(new PointerEvent("pointercancel", {
|
|
5796
|
-
pointerId: pressed,
|
|
5797
|
-
isPrimary: true
|
|
5798
|
-
}));
|
|
5799
|
-
swallow.add(pressed);
|
|
5800
|
-
pressed = void 0;
|
|
5801
|
-
}
|
|
6375
|
+
presses.cancel();
|
|
6376
|
+
cancelPressed();
|
|
5802
6377
|
editingMade = false;
|
|
5803
6378
|
madeEdit?.reset();
|
|
5804
6379
|
current?.deactivate();
|
|
@@ -5808,8 +6383,22 @@ function createToolController$1(opts) {
|
|
|
5808
6383
|
next.activate(ctx);
|
|
5809
6384
|
ctx.root.dataset.inkTool = id;
|
|
5810
6385
|
ctx.root.style.cursor = next.cursor;
|
|
6386
|
+
applyClaim();
|
|
5811
6387
|
active.set(id);
|
|
5812
6388
|
}
|
|
6389
|
+
/** Ends the live press's gesture, if any, and keeps its release from every tool. */
|
|
6390
|
+
function cancelPressed() {
|
|
6391
|
+
if (pressed === void 0) return;
|
|
6392
|
+
if (editingMade) {
|
|
6393
|
+
editingMade = false;
|
|
6394
|
+
madeEdit?.onPointerCancel();
|
|
6395
|
+
} else current?.onPointerCancel?.(new PointerEvent("pointercancel", {
|
|
6396
|
+
pointerId: pressed,
|
|
6397
|
+
isPrimary: true
|
|
6398
|
+
}));
|
|
6399
|
+
swallow.add(pressed);
|
|
6400
|
+
pressed = void 0;
|
|
6401
|
+
}
|
|
5813
6402
|
set(opts.initial ?? "select-text");
|
|
5814
6403
|
if (current === void 0 && opts.initial !== void 0 && opts.initial !== "select-text") set("select-text");
|
|
5815
6404
|
return {
|
|
@@ -5827,11 +6416,12 @@ function createToolController$1(opts) {
|
|
|
5827
6416
|
return registry.readable(id);
|
|
5828
6417
|
},
|
|
5829
6418
|
destroy() {
|
|
6419
|
+
presses.destroy();
|
|
5830
6420
|
madeEdit?.reset();
|
|
5831
6421
|
madeEdit = void 0;
|
|
5832
6422
|
current?.deactivate();
|
|
5833
|
-
clearMade();
|
|
5834
6423
|
current = void 0;
|
|
6424
|
+
clearMade();
|
|
5835
6425
|
doc.defaultView?.removeEventListener("pointerdown", readFirst, { capture: true });
|
|
5836
6426
|
doc.removeEventListener("pointerdown", readAfterEditor, { capture: true });
|
|
5837
6427
|
ctx.root.removeEventListener("pointerdown", onDown, { capture: true });
|
|
@@ -5841,6 +6431,7 @@ function createToolController$1(opts) {
|
|
|
5841
6431
|
ctx.root.removeEventListener("lostpointercapture", onCancel);
|
|
5842
6432
|
ctx.root.removeEventListener("keydown", onKey);
|
|
5843
6433
|
ctx.root.removeEventListener("keydown", onEditorEscape, { capture: true });
|
|
6434
|
+
ctx.root.removeEventListener("touchmove", onTouchMove);
|
|
5844
6435
|
delete ctx.root.dataset.inkTool;
|
|
5845
6436
|
ctx.root.style.cursor = originalCursor;
|
|
5846
6437
|
}
|
|
@@ -5861,6 +6452,18 @@ function isViewportEnter(e, root) {
|
|
|
5861
6452
|
if (target.matches(".ink-page[data-page]")) return true;
|
|
5862
6453
|
return target.closest("[data-ink-text-layer], [data-ink-canvas-layer]") !== null;
|
|
5863
6454
|
}
|
|
6455
|
+
function pointerTypeOf(e) {
|
|
6456
|
+
return e.pointerType === "touch" || e.pointerType === "pen" ? e.pointerType : "mouse";
|
|
6457
|
+
}
|
|
6458
|
+
/**
|
|
6459
|
+
* True when a press on `target` commits an open free-text editor (which has focus while open) or
|
|
6460
|
+
* closes an open Inkbar popover (a press outside it does).
|
|
6461
|
+
*/
|
|
6462
|
+
function dismisses(target, doc) {
|
|
6463
|
+
if (doc.activeElement?.closest("[data-ink-editor]")) return true;
|
|
6464
|
+
const node = target instanceof Node ? target : null;
|
|
6465
|
+
return [...doc.querySelectorAll("[data-ink-popover]")].some((panel) => !panel.hidden && !panel.contains(node));
|
|
6466
|
+
}
|
|
5864
6467
|
/**
|
|
5865
6468
|
* True for a press on `root` itself outside its client area: on a scroll bar (or its border).
|
|
5866
6469
|
* Client coordinates only, so a synthetic press gives the same answer as a real one.
|
|
@@ -5914,6 +6517,7 @@ function freehandTool(opts) {
|
|
|
5914
6517
|
/** Adds a sample. A release reports 0 pressure, so it repeats the last sample's instead. */
|
|
5915
6518
|
const sample = (s, e, release = false) => {
|
|
5916
6519
|
const css = clampedCssIn(s.from.host, e);
|
|
6520
|
+
s.travel = Math.max(s.travel, Math.hypot(e.clientX - s.origin.x, e.clientY - s.origin.y));
|
|
5917
6521
|
s.css.push(css);
|
|
5918
6522
|
s.user.push(s.from.transform.fromCss(css));
|
|
5919
6523
|
const p = release ? s.pressure?.[s.pressure.length - 1] : pressureOf(e);
|
|
@@ -5956,6 +6560,7 @@ function freehandTool(opts) {
|
|
|
5956
6560
|
label: opts.label,
|
|
5957
6561
|
icon: opts.icon,
|
|
5958
6562
|
kind: "sticky",
|
|
6563
|
+
claimsTouch: true,
|
|
5959
6564
|
cursor: "crosshair",
|
|
5960
6565
|
styleGroup: "ink",
|
|
5961
6566
|
activate(c) {
|
|
@@ -5974,7 +6579,13 @@ function freehandTool(opts) {
|
|
|
5974
6579
|
css: [],
|
|
5975
6580
|
user: [],
|
|
5976
6581
|
pressure: void 0,
|
|
5977
|
-
startedAt: Date.now()
|
|
6582
|
+
startedAt: Date.now(),
|
|
6583
|
+
pointerType: e.pointerType,
|
|
6584
|
+
origin: {
|
|
6585
|
+
x: e.clientX,
|
|
6586
|
+
y: e.clientY
|
|
6587
|
+
},
|
|
6588
|
+
travel: 0
|
|
5978
6589
|
};
|
|
5979
6590
|
sample(stroke, e);
|
|
5980
6591
|
e.stopPropagation();
|
|
@@ -6005,6 +6616,7 @@ function freehandTool(opts) {
|
|
|
6005
6616
|
const end = clampedCssIn(s.from.host, e);
|
|
6006
6617
|
if (end.x !== last.x || end.y !== last.y) sample(s, e, true);
|
|
6007
6618
|
clear();
|
|
6619
|
+
if (s.pointerType === "touch" && s.travel <= 10) return;
|
|
6008
6620
|
opts.commit(ctx, {
|
|
6009
6621
|
page: s.from.page,
|
|
6010
6622
|
points: s.user,
|
|
@@ -8287,6 +8899,7 @@ function freetextTool() {
|
|
|
8287
8899
|
label: "Text box",
|
|
8288
8900
|
icon: Type,
|
|
8289
8901
|
kind: "one-shot",
|
|
8902
|
+
claimsTouch: true,
|
|
8290
8903
|
cursor: "text",
|
|
8291
8904
|
styleGroup: "text",
|
|
8292
8905
|
defaultOptions: {},
|
|
@@ -8543,6 +9156,7 @@ function redactAreaTool() {
|
|
|
8543
9156
|
label: "Mark for redaction",
|
|
8544
9157
|
icon: Square,
|
|
8545
9158
|
kind: "one-shot",
|
|
9159
|
+
claimsTouch: true,
|
|
8546
9160
|
cursor: "crosshair",
|
|
8547
9161
|
styleGroup: "redact",
|
|
8548
9162
|
activate(c) {
|
|
@@ -8833,6 +9447,7 @@ function shapeTool() {
|
|
|
8833
9447
|
icon: Shapes,
|
|
8834
9448
|
kind: "one-shot",
|
|
8835
9449
|
cursor: "crosshair",
|
|
9450
|
+
claimsTouch: true,
|
|
8836
9451
|
styleGroup: "shape",
|
|
8837
9452
|
defaultOptions: { ...DEFAULT_SHAPE_OPTIONS },
|
|
8838
9453
|
activate(c) {
|
|
@@ -9383,6 +9998,7 @@ function mountViewer(opts) {
|
|
|
9383
9998
|
const pages = /* @__PURE__ */ new Map();
|
|
9384
9999
|
let gap = opts.gap;
|
|
9385
10000
|
let buffer = opts.buffer;
|
|
10001
|
+
let pinchZoom = opts.pinchZoom;
|
|
9386
10002
|
let custom = opts.customAnnotations ?? false;
|
|
9387
10003
|
let viewport;
|
|
9388
10004
|
let tools;
|
|
@@ -9637,6 +10253,7 @@ function mountViewer(opts) {
|
|
|
9637
10253
|
...state.rotation === void 0 ? {} : { rotation: state.rotation },
|
|
9638
10254
|
...gap === void 0 ? {} : { gap },
|
|
9639
10255
|
...buffer === void 0 ? {} : { buffer },
|
|
10256
|
+
...pinchZoom === void 0 ? {} : { pinchZoom },
|
|
9640
10257
|
onMount: mountPage,
|
|
9641
10258
|
onUpdate: (page, _host, transform) => pages.get(page)?.update(transform),
|
|
9642
10259
|
onUnmount: unmountPage
|
|
@@ -9646,6 +10263,7 @@ function mountViewer(opts) {
|
|
|
9646
10263
|
...state.tool === void 0 ? {} : { initial: state.tool },
|
|
9647
10264
|
tools: list,
|
|
9648
10265
|
options: toolOptions,
|
|
10266
|
+
...plugins && { onPress: (kind, press) => plugins.emitPress(kind, press) },
|
|
9649
10267
|
ctx: {
|
|
9650
10268
|
toolStyle,
|
|
9651
10269
|
store: doc.annotations,
|
|
@@ -9731,6 +10349,7 @@ function mountViewer(opts) {
|
|
|
9731
10349
|
teardown();
|
|
9732
10350
|
if (next.gap !== void 0) gap = next.gap;
|
|
9733
10351
|
if (next.buffer !== void 0) buffer = next.buffer;
|
|
10352
|
+
if (next.pinchZoom !== void 0) pinchZoom = next.pinchZoom;
|
|
9734
10353
|
if (next.customAnnotations !== void 0) custom = next.customAnnotations;
|
|
9735
10354
|
build(state);
|
|
9736
10355
|
},
|
|
@@ -14501,6 +15120,6 @@ function createHighlightMenu(viewer) {
|
|
|
14501
15120
|
return root;
|
|
14502
15121
|
}
|
|
14503
15122
|
//#endregion
|
|
14504
|
-
export { ARROWHEAD_CHOICES, ARROWHEAD_ENDING, COLOR_COLUMNS, COLOR_LABELS, DASH_PATTERN, DEFAULT_CORNER_RADIUS, DEFAULT_FREETEXT_WIDTH, DEFAULT_HIGHLIGHT_OPTIONS, DEFAULT_SHAPE_SIZE, FONT_FAMILIES, FORM_LABELS, LINE_WEIGHTS, MARKUP_ICONS, MARKUP_KINDS, MARKUP_LABELS, PALETTE, SHAPE_COLUMNS, SHAPE_ICONS, SHAPE_KINDS, SHAPE_LABELS, SIGNATURE_FONTS, SIGNATURE_IMAGE_MAX_LENGTH, SIGNATURE_IMAGE_MAX_SIDE, SIGNATURE_IMAGE_TYPES, SIGNATURE_LABELS, SIGNATURE_TABS, THUMBNAIL_WIDTH, TOOL_LABELS, ZOOM_LEVELS, allTools, annotationElementFor, arrowheadChoice, attachFileKeys, attachHistoryKeys, attachViewportKeys, clampFontSize, colorMenuModel, computeCssLayout, createColorMenu, createCommandButton, createDownloadButton, createFindBar, createFocusReturn, createFormAlert, createHighlightMenu, createIcon, createImageSignature, createLineStyleMenu, createMenu, createNextPage, createOverlay, createPageCount, createPageInput, createPageMount, createPageRenderer, createPageTransform, createPluginHost, createPluginLayers, createPopover, createPrevPage, createPrintButton, createRecordList, createRecordListController, createRedo, createSearchButton, createSearchController, createSearchResults, createShapeMenu, createSidebar, createSidebarController, createSignFieldMenu, createSignatureDialog, createSignatureDraft, createSignatureMenu, createSignaturePad, createSignaturePreview, createTextLayer, createTextStyleMenu, createThumbnail, createThumbnailPainter, createThumbnails, createThumbnailsController, createToolButton, createToolController, createToolOptions, createToolbar, createToolbarSeparator, createTypedSignature, createUndo, createViewer, createViewport, createZoomIn, createZoomOut, createZoomSelect, cssRectsToUserRects, cssVariableDefaults, definePlugin, downloadDocument, drawTool, editFreeText, findAnnouncer, findCountText, fitZoomCss, focusInitialMenuItem, fontFamilyValue, freetextTool, getSelectionQuads, handleFindKey, handleMenuKeys, handleRadioKeys, handleResultsFieldKey, handleTabKeys, highlightMenuModel, highlightTool, hitFor, isBlankQuery, keepHistoryFocus, lineStyleMenuModel, menuItems, mostVisiblePageCss, mountViewer, normalizeRotation, noteTool, observeAnnotationElement, pageSelectionText, placePanel, printDocument, recordsSource, redactAreaTool, renderAnnotationContent, renderTypedSignature, searchSource, selectTextTool, selectTool, shapeMenuModel, shapeTool, shouldKeepOpenOnAnnotation, sidebarBadge, signField, signatureMenuModel, signaturePreview, signatureTool, sketchTool, textStyleMenuModel, threadDeleteLabel, threadPopoverLabel, thumbnailSize, trackToolStyle, trapTab, unknownCommand, unknownTool, viewerSearchOf, visibleRangeCss, watchDetached, withHostTools };
|
|
15123
|
+
export { ARROWHEAD_CHOICES, ARROWHEAD_ENDING, COLOR_COLUMNS, COLOR_LABELS, DASH_PATTERN, DEFAULT_CORNER_RADIUS, DEFAULT_FREETEXT_WIDTH, DEFAULT_HIGHLIGHT_OPTIONS, DEFAULT_SHAPE_SIZE, FONT_FAMILIES, FORM_LABELS, LINE_WEIGHTS, MARKUP_ICONS, MARKUP_KINDS, MARKUP_LABELS, PALETTE, SHAPE_COLUMNS, SHAPE_ICONS, SHAPE_KINDS, SHAPE_LABELS, SIGNATURE_FONTS, SIGNATURE_IMAGE_MAX_LENGTH, SIGNATURE_IMAGE_MAX_SIDE, SIGNATURE_IMAGE_TYPES, SIGNATURE_LABELS, SIGNATURE_TABS, THUMBNAIL_WIDTH, TOOL_LABELS, ZOOM_LEVELS, allTools, annotationElementFor, arrowheadChoice, attachFileKeys, attachHistoryKeys, attachTouchPinch, attachTrackpadPinch, attachViewportKeys, clampFontSize, colorMenuModel, computeCssLayout, createColorMenu, createCommandButton, createDownloadButton, createFindBar, createFocusReturn, createFormAlert, createHighlightMenu, createIcon, createImageSignature, createLineStyleMenu, createMenu, createNextPage, createOverlay, createPageCount, createPageInput, createPageMount, createPageRenderer, createPageTransform, createPluginHost, createPluginLayers, createPopover, createPrevPage, createPrintButton, createRecordList, createRecordListController, createRedo, createSearchButton, createSearchController, createSearchResults, createShapeMenu, createSidebar, createSidebarController, createSignFieldMenu, createSignatureDialog, createSignatureDraft, createSignatureMenu, createSignaturePad, createSignaturePreview, createTextLayer, createTextStyleMenu, createThumbnail, createThumbnailPainter, createThumbnails, createThumbnailsController, createToolButton, createToolController, createToolOptions, createToolbar, createToolbarSeparator, createTypedSignature, createUndo, createViewer, createViewport, createZoomIn, createZoomOut, createZoomPreview, createZoomSelect, cssRectsToUserRects, cssVariableDefaults, definePlugin, downloadDocument, drawTool, editFreeText, findAnnouncer, findCountText, fitZoomCss, focusInitialMenuItem, fontFamilyValue, freetextTool, getSelectionQuads, handleFindKey, handleMenuKeys, handleRadioKeys, handleResultsFieldKey, handleTabKeys, highlightMenuModel, highlightTool, hitFor, isBlankQuery, keepHistoryFocus, lineStyleMenuModel, menuItems, mostVisiblePageCss, mountViewer, normalizeRotation, noteTool, observeAnnotationElement, pageSelectionText, pinchFrame, placePanel, printDocument, recordsSource, redactAreaTool, renderAnnotationContent, renderTypedSignature, searchSource, selectTextTool, selectTool, shapeMenuModel, shapeTool, shouldKeepOpenOnAnnotation, sidebarBadge, signField, signatureMenuModel, signaturePreview, signatureTool, sketchTool, textStyleMenuModel, threadDeleteLabel, threadPopoverLabel, thumbnailSize, trackToolStyle, trapTab, unknownCommand, unknownTool, viewerSearchOf, visibleRangeCss, watchDetached, wheelFactor, withHostTools };
|
|
14505
15124
|
|
|
14506
15125
|
//# sourceMappingURL=dom.mjs.map
|