@inkbar/core 0.6.0 → 0.7.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 +39 -1
- package/dist/dom.d.mts +124 -3
- package/dist/dom.d.mts.map +1 -1
- package/dist/dom.mjs +649 -10
- package/dist/dom.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{signatures-By4B40DP.d.mts → signatures-CalOktkf.d.mts} +65 -12
- package/dist/signatures-CalOktkf.d.mts.map +1 -0
- package/dist/{signatures-D68fuLlt.mjs → signatures-Do_l2IlM.mjs} +617 -9
- package/dist/signatures-Do_l2IlM.mjs.map +1 -0
- package/package.json +4 -3
- package/dist/signatures-By4B40DP.d.mts.map +0 -1
- package/dist/signatures-D68fuLlt.mjs.map +0 -1
package/dist/dom.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $t as
|
|
2
|
-
import { ALargeSmall, AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, Check, ChevronDown, ChevronLeft, ChevronRight, Circle, CircleDashed, Hexagon, Highlighter, ICON_ATTRIBUTES, Italic, MessageCircle, MessageSquare, MessageSquareText, Minus, MoveUpRight, PenLine, PenTool, Plus, RectangleHorizontal, Redo2, Search, Signature, Slash, SpellCheck2, Square, SquareDashed, SquareDashedMousePointer, Star, Strikethrough, TextCursor, Trash2, Type, Underline, Undo2, ZoomIn, ZoomOut } from "@inkbar/icons";
|
|
1
|
+
import { $t as createRecordStore, B as isTextMarkup, Bt as isStandardCallout, E as withoutReplies, G as resizedRect, H as movedEndpoint, I as hasCallout, J as withBox, K as scaled, Kt as isKnown, L as hasCornerHandles, O as createToolStyle, Q as deriveRect, Qt as mergeLineRects, R as hasRectGeometry, Rt as calloutStroke, S as isThreadRoot, T as threadRoot, U as movedTip, Ut as LINE_ENDINGS, V as keepsAspect, Vt as withCalloutBounds, W as resizedBox, X as isBlankQuery, Y as openDocument, Yt as cssVariableDefaults, _ as defaultEnds, an as invertMatrix, b as isAbandonedNote, bn as InkError, d as recogniseStroke, en as derived, f as DEFAULT_CORNER_RADIUS, fn as rectFromPoints, g as constrain, h as SHAPE_KINDS, hn as rectsIntersect, i as createInkTemplate, l as placeSignature, m as DEFAULT_SHAPE_SIZE, nn as writable, nt as isProduction, o as createMemorySignatureStore, p as DEFAULT_SHAPE_OPTIONS, q as translated, r as createImageTemplate, rn as applyMatrix, s as createSignatureLibrary, tn as readonly, v as isPointerOnlyKind, w as threadOf, xn as isInkError, y as shapeInput, z as isBubble, zt as freeTextBox } from "./signatures-Do_l2IlM.mjs";
|
|
2
|
+
import { ALargeSmall, AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, CaseSensitive, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Circle, CircleDashed, Hexagon, Highlighter, ICON_ATTRIBUTES, Italic, MessageCircle, MessageSquare, MessageSquareText, Minus, MoveUpRight, PenLine, PenTool, Plus, RectangleHorizontal, Redo2, Search, Signature, Slash, SpellCheck2, Square, SquareDashed, SquareDashedMousePointer, Star, Strikethrough, TextCursor, Trash2, Type, Underline, Undo2, X, ZoomIn, ZoomOut } from "@inkbar/icons";
|
|
3
3
|
import { getStroke } from "perfect-freehand";
|
|
4
4
|
//#region src/dom/transform.ts
|
|
5
5
|
/** Folds any integer multiple of 90 onto 0, 90, 180 or 270. Throws `RangeError` otherwise. */
|
|
@@ -3803,6 +3803,23 @@ function createPluginLayers(source) {
|
|
|
3803
3803
|
}
|
|
3804
3804
|
//#endregion
|
|
3805
3805
|
//#region src/dom/plugins/host.ts
|
|
3806
|
+
/** What `ctx.search` is when the host has no search: closed, and every action does nothing. */
|
|
3807
|
+
const inertSearch = {
|
|
3808
|
+
state: readonly(writable({
|
|
3809
|
+
open: false,
|
|
3810
|
+
query: "",
|
|
3811
|
+
matchCase: false,
|
|
3812
|
+
hits: [],
|
|
3813
|
+
current: -1,
|
|
3814
|
+
scanning: false
|
|
3815
|
+
})),
|
|
3816
|
+
open() {},
|
|
3817
|
+
setQuery() {},
|
|
3818
|
+
setMatchCase() {},
|
|
3819
|
+
next() {},
|
|
3820
|
+
previous() {},
|
|
3821
|
+
close() {}
|
|
3822
|
+
};
|
|
3806
3823
|
/**
|
|
3807
3824
|
* `value` seen with `extra` laid over it, read-only. Everything else is read from and written to
|
|
3808
3825
|
* `value` itself, and its methods are bound to it, so a method always runs on the original
|
|
@@ -3960,7 +3977,8 @@ function createPluginHost(opts) {
|
|
|
3960
3977
|
},
|
|
3961
3978
|
scrollTo(page, rect, scrollOpts) {
|
|
3962
3979
|
opts.viewport?.()?.scrollToRect(page, rect, scrollOpts);
|
|
3963
|
-
}
|
|
3980
|
+
},
|
|
3981
|
+
search: opts.search ?? inertSearch
|
|
3964
3982
|
};
|
|
3965
3983
|
};
|
|
3966
3984
|
for (const plugin of opts.plugins) {
|
|
@@ -4053,6 +4071,7 @@ function createPageMount(opts) {
|
|
|
4053
4071
|
transform,
|
|
4054
4072
|
...callbacks("text")
|
|
4055
4073
|
});
|
|
4074
|
+
const marks = opts.highlights?.mount(page, host, transform);
|
|
4056
4075
|
const overlay = createOverlay({
|
|
4057
4076
|
host,
|
|
4058
4077
|
page,
|
|
@@ -4074,10 +4093,12 @@ function createPageMount(opts) {
|
|
|
4074
4093
|
textLayer.update(next);
|
|
4075
4094
|
overlay.update(next);
|
|
4076
4095
|
layers?.update(next);
|
|
4096
|
+
marks?.update(next);
|
|
4077
4097
|
},
|
|
4078
4098
|
destroy() {
|
|
4079
4099
|
layers?.destroy();
|
|
4080
4100
|
overlay.destroy();
|
|
4101
|
+
marks?.destroy();
|
|
4081
4102
|
textLayer.destroy();
|
|
4082
4103
|
renderer.destroy();
|
|
4083
4104
|
}
|
|
@@ -6629,6 +6650,109 @@ function trapTab(e, container) {
|
|
|
6629
6650
|
}
|
|
6630
6651
|
}
|
|
6631
6652
|
//#endregion
|
|
6653
|
+
//#region src/dom/search/highlights.ts
|
|
6654
|
+
const NO_HITS = [];
|
|
6655
|
+
/** One box over the quad: its CSS bounding box, clipped to the quad when it is not upright. */
|
|
6656
|
+
function hitBox(doc, t, quad) {
|
|
6657
|
+
const { p1, p2, p3, p4 } = t.toCssQuad(quad);
|
|
6658
|
+
const xs = [
|
|
6659
|
+
p1.x,
|
|
6660
|
+
p2.x,
|
|
6661
|
+
p3.x,
|
|
6662
|
+
p4.x
|
|
6663
|
+
];
|
|
6664
|
+
const ys = [
|
|
6665
|
+
p1.y,
|
|
6666
|
+
p2.y,
|
|
6667
|
+
p3.y,
|
|
6668
|
+
p4.y
|
|
6669
|
+
];
|
|
6670
|
+
const left = Math.min(...xs);
|
|
6671
|
+
const top = Math.min(...ys);
|
|
6672
|
+
const width = Math.max(...xs) - left;
|
|
6673
|
+
const height = Math.max(...ys) - top;
|
|
6674
|
+
const el = doc.createElement("div");
|
|
6675
|
+
el.dataset.searchHit = "";
|
|
6676
|
+
let css = `position:absolute;left:${left}px;top:${top}px;width:${width}px;height:${height}px;`;
|
|
6677
|
+
if (!(Math.abs(p1.y - p2.y) < .01 && Math.abs(p3.y - p4.y) < .01 || Math.abs(p1.x - p2.x) < .01 && Math.abs(p3.x - p4.x) < .01) && width > 0 && height > 0) {
|
|
6678
|
+
const pt = (p) => `${((p.x - left) / width * 100).toFixed(3)}% ${((p.y - top) / height * 100).toFixed(3)}%`;
|
|
6679
|
+
css += `clip-path:polygon(${[
|
|
6680
|
+
p1,
|
|
6681
|
+
p2,
|
|
6682
|
+
p4,
|
|
6683
|
+
p3
|
|
6684
|
+
].map(pt).join(",")});`;
|
|
6685
|
+
}
|
|
6686
|
+
el.style.cssText = css;
|
|
6687
|
+
return el;
|
|
6688
|
+
}
|
|
6689
|
+
function createSearchHighlights(search) {
|
|
6690
|
+
const pages = /* @__PURE__ */ new Map();
|
|
6691
|
+
const sameHits = (a, b) => a.length === b.length && a.every((hit, i) => hit === b[i]);
|
|
6692
|
+
const draw = (page, force, hits) => {
|
|
6693
|
+
const { current: index, hits: all } = search.state.get();
|
|
6694
|
+
const current = all[index];
|
|
6695
|
+
const shown = current && hits.includes(current) ? current : void 0;
|
|
6696
|
+
if (!force && sameHits(page.hits, hits) && page.current === shown) return;
|
|
6697
|
+
page.hits = hits;
|
|
6698
|
+
page.current = shown;
|
|
6699
|
+
const doc = page.el.ownerDocument;
|
|
6700
|
+
page.el.replaceChildren(...hits.flatMap((hit) => hit.quads.map((quad) => {
|
|
6701
|
+
const box = hitBox(doc, page.transform, quad);
|
|
6702
|
+
if (hit === shown) box.dataset.current = "";
|
|
6703
|
+
return box;
|
|
6704
|
+
})));
|
|
6705
|
+
};
|
|
6706
|
+
/** The hits on `page`: a run of the state's hits, which are in `(page, start)` order. */
|
|
6707
|
+
const hitsOn = (all, page) => {
|
|
6708
|
+
const first = (p) => {
|
|
6709
|
+
let low = 0;
|
|
6710
|
+
let high = all.length;
|
|
6711
|
+
while (low < high) {
|
|
6712
|
+
const mid = low + high >>> 1;
|
|
6713
|
+
if (all[mid].page < p) low = mid + 1;
|
|
6714
|
+
else high = mid;
|
|
6715
|
+
}
|
|
6716
|
+
return low;
|
|
6717
|
+
};
|
|
6718
|
+
const from = first(page);
|
|
6719
|
+
const to = first(page + 1);
|
|
6720
|
+
return to > from ? all.slice(from, to) : NO_HITS;
|
|
6721
|
+
};
|
|
6722
|
+
return {
|
|
6723
|
+
mount(index, host, transform) {
|
|
6724
|
+
const el = host.ownerDocument.createElement("div");
|
|
6725
|
+
el.className = "ink-search-layer";
|
|
6726
|
+
el.dataset.inkSearchLayer = "";
|
|
6727
|
+
el.setAttribute("aria-hidden", "true");
|
|
6728
|
+
el.style.cssText = "position:absolute;inset:0;pointer-events:none;";
|
|
6729
|
+
const overlay = host.querySelector(":scope > .ink-overlay");
|
|
6730
|
+
host.insertBefore(el, overlay);
|
|
6731
|
+
const page = {
|
|
6732
|
+
el,
|
|
6733
|
+
transform,
|
|
6734
|
+
hits: NO_HITS,
|
|
6735
|
+
current: void 0
|
|
6736
|
+
};
|
|
6737
|
+
pages.set(index, page);
|
|
6738
|
+
draw(page, true, hitsOn(search.state.get().hits, index));
|
|
6739
|
+
return {
|
|
6740
|
+
update(next) {
|
|
6741
|
+
page.transform = next;
|
|
6742
|
+
draw(page, true, page.hits);
|
|
6743
|
+
},
|
|
6744
|
+
destroy() {
|
|
6745
|
+
if (pages.get(index) === page) pages.delete(index);
|
|
6746
|
+
el.remove();
|
|
6747
|
+
}
|
|
6748
|
+
};
|
|
6749
|
+
},
|
|
6750
|
+
destroy: search.state.subscribe(({ hits }) => {
|
|
6751
|
+
for (const [index, page] of pages) draw(page, false, hitsOn(hits, index));
|
|
6752
|
+
})
|
|
6753
|
+
};
|
|
6754
|
+
}
|
|
6755
|
+
//#endregion
|
|
6632
6756
|
//#region src/dom/viewer-engine.ts
|
|
6633
6757
|
function mountViewer(opts) {
|
|
6634
6758
|
const { container: el, document: doc } = opts;
|
|
@@ -6645,6 +6769,8 @@ function mountViewer(opts) {
|
|
|
6645
6769
|
const hostTools = [...opts.tools ?? [], ...plugins?.tools ?? []];
|
|
6646
6770
|
/** Plugin layers follow the host for the engine's life, across rebuilds. */
|
|
6647
6771
|
const layers = plugins && createPluginLayers(plugins);
|
|
6772
|
+
/** Match highlights follow the controller for the engine's life, across rebuilds. */
|
|
6773
|
+
const highlights = opts.search && createSearchHighlights(opts.search);
|
|
6648
6774
|
const decorate = plugins ? (a) => plugins.decorate(a) : (a) => opts.decorateAnnotation?.(a);
|
|
6649
6775
|
/** A plugin's `ctx.redecorate()` reaches every page this engine has mounted. */
|
|
6650
6776
|
const stopRedecorate = plugins?.watchRedecorate(() => {
|
|
@@ -6680,6 +6806,7 @@ function mountViewer(opts) {
|
|
|
6680
6806
|
isCustom: () => isCustom,
|
|
6681
6807
|
decorate,
|
|
6682
6808
|
...layers ? { layers } : {},
|
|
6809
|
+
...highlights ? { highlights } : {},
|
|
6683
6810
|
onError: (cause, layer) => {
|
|
6684
6811
|
failed.add(layer);
|
|
6685
6812
|
if (error) return;
|
|
@@ -6865,11 +6992,292 @@ function mountViewer(opts) {
|
|
|
6865
6992
|
destroyed = true;
|
|
6866
6993
|
teardown();
|
|
6867
6994
|
layers?.destroy();
|
|
6995
|
+
highlights?.destroy();
|
|
6868
6996
|
stopRedecorate?.();
|
|
6869
6997
|
}
|
|
6870
6998
|
};
|
|
6871
6999
|
}
|
|
6872
7000
|
//#endregion
|
|
7001
|
+
//#region src/dom/search/controller.ts
|
|
7002
|
+
function compare(a, b) {
|
|
7003
|
+
return a.page - b.page || a.start - b.start;
|
|
7004
|
+
}
|
|
7005
|
+
/** Where `hit` goes in `hits`, which is sorted by `(page, start)`. */
|
|
7006
|
+
function insertionIndex(hits, hit) {
|
|
7007
|
+
let low = 0;
|
|
7008
|
+
let high = hits.length;
|
|
7009
|
+
while (low < high) {
|
|
7010
|
+
const mid = low + high >>> 1;
|
|
7011
|
+
if (compare(hits[mid], hit) <= 0) low = mid + 1;
|
|
7012
|
+
else high = mid;
|
|
7013
|
+
}
|
|
7014
|
+
return low;
|
|
7015
|
+
}
|
|
7016
|
+
function createSearchController(opts) {
|
|
7017
|
+
const debounceMs = opts.debounceMs ?? 150;
|
|
7018
|
+
const state = writable({
|
|
7019
|
+
open: false,
|
|
7020
|
+
query: "",
|
|
7021
|
+
matchCase: false,
|
|
7022
|
+
hits: [],
|
|
7023
|
+
current: -1,
|
|
7024
|
+
scanning: false
|
|
7025
|
+
});
|
|
7026
|
+
const bars = writable(0);
|
|
7027
|
+
const patch = (changes) => state.update((s) => ({
|
|
7028
|
+
...s,
|
|
7029
|
+
...changes
|
|
7030
|
+
}));
|
|
7031
|
+
let timer;
|
|
7032
|
+
let scan;
|
|
7033
|
+
/** The current hit and its index in `hits`, kept in step as pages of hits arrive. */
|
|
7034
|
+
let currentHit;
|
|
7035
|
+
let currentIndex = -1;
|
|
7036
|
+
/** Hits of one page not yet in the state, in page order, and the task that adds them. */
|
|
7037
|
+
let pending = [];
|
|
7038
|
+
let flushTimer;
|
|
7039
|
+
let destroyed = false;
|
|
7040
|
+
function scrollTo(hit) {
|
|
7041
|
+
opts.viewport()?.scrollToRect(hit.page, hit.rect);
|
|
7042
|
+
}
|
|
7043
|
+
/** Stops the pending debounce and the running scan, and empties the hits. */
|
|
7044
|
+
function reset() {
|
|
7045
|
+
clearTimeout(timer);
|
|
7046
|
+
timer = void 0;
|
|
7047
|
+
scan?.abort();
|
|
7048
|
+
scan = void 0;
|
|
7049
|
+
currentHit = void 0;
|
|
7050
|
+
currentIndex = -1;
|
|
7051
|
+
pending = [];
|
|
7052
|
+
clearTimeout(flushTimer);
|
|
7053
|
+
flushTimer = void 0;
|
|
7054
|
+
patch({
|
|
7055
|
+
hits: [],
|
|
7056
|
+
current: -1,
|
|
7057
|
+
scanning: false
|
|
7058
|
+
});
|
|
7059
|
+
}
|
|
7060
|
+
/**
|
|
7061
|
+
* Adds the pending page of hits to the state in one change. A page's hits sit together in
|
|
7062
|
+
* `(page, start)` order, so they go in with one splice, and `current` moves by their count
|
|
7063
|
+
* when they land before it. The first hit becomes current and is scrolled to, unless
|
|
7064
|
+
* `scroll` is false: a step flushes first and scrolls to where it lands instead.
|
|
7065
|
+
*/
|
|
7066
|
+
function flush(scroll = true) {
|
|
7067
|
+
clearTimeout(flushTimer);
|
|
7068
|
+
flushTimer = void 0;
|
|
7069
|
+
if (pending.length === 0) return;
|
|
7070
|
+
const batch = pending;
|
|
7071
|
+
pending = [];
|
|
7072
|
+
const first = batch[0];
|
|
7073
|
+
const { hits } = state.get();
|
|
7074
|
+
const at = insertionIndex(hits, first);
|
|
7075
|
+
const next = hits.slice(0, at).concat(batch, hits.slice(at));
|
|
7076
|
+
if (!currentHit) {
|
|
7077
|
+
currentHit = first;
|
|
7078
|
+
currentIndex = at;
|
|
7079
|
+
if (scroll) scrollTo(first);
|
|
7080
|
+
} else if (at <= currentIndex) currentIndex += batch.length;
|
|
7081
|
+
patch({
|
|
7082
|
+
hits: next,
|
|
7083
|
+
current: currentIndex
|
|
7084
|
+
});
|
|
7085
|
+
}
|
|
7086
|
+
/**
|
|
7087
|
+
* Holds `hit` until its page is done: the next page's first hit, the end of the scan, or the
|
|
7088
|
+
* next task, since a page's hits arrive together and the scan yields between pages.
|
|
7089
|
+
*/
|
|
7090
|
+
function accept(hit) {
|
|
7091
|
+
if (pending.length > 0 && pending[0].page !== hit.page) flush();
|
|
7092
|
+
pending.push(hit);
|
|
7093
|
+
flushTimer ??= setTimeout(() => flush());
|
|
7094
|
+
}
|
|
7095
|
+
async function run(doc, controller) {
|
|
7096
|
+
const { query, matchCase } = state.get();
|
|
7097
|
+
const count = doc.pages.length;
|
|
7098
|
+
const from = Math.min(Math.max(opts.viewport()?.currentPage.get() ?? 0, 0), Math.max(count - 1, 0));
|
|
7099
|
+
const pages = [...Array.from({ length: count - from }, (_, i) => from + i), ...Array.from({ length: from }, (_, i) => i)];
|
|
7100
|
+
try {
|
|
7101
|
+
for await (const hit of doc.search(query, {
|
|
7102
|
+
matchCase,
|
|
7103
|
+
pages,
|
|
7104
|
+
signal: controller.signal
|
|
7105
|
+
})) {
|
|
7106
|
+
if (controller.signal.aborted) return;
|
|
7107
|
+
accept(hit);
|
|
7108
|
+
}
|
|
7109
|
+
} catch (error) {
|
|
7110
|
+
if (!controller.signal.aborted) {
|
|
7111
|
+
console.error(error);
|
|
7112
|
+
if (scan === controller) scan = void 0;
|
|
7113
|
+
flush();
|
|
7114
|
+
patch({ scanning: false });
|
|
7115
|
+
return;
|
|
7116
|
+
}
|
|
7117
|
+
}
|
|
7118
|
+
if (scan === controller) {
|
|
7119
|
+
scan = void 0;
|
|
7120
|
+
flush();
|
|
7121
|
+
patch({ scanning: false });
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
function start() {
|
|
7125
|
+
timer = void 0;
|
|
7126
|
+
scan?.abort();
|
|
7127
|
+
scan = void 0;
|
|
7128
|
+
const doc = opts.document.get();
|
|
7129
|
+
if (!doc || isBlankQuery(state.get().query)) {
|
|
7130
|
+
patch({ scanning: false });
|
|
7131
|
+
return;
|
|
7132
|
+
}
|
|
7133
|
+
const controller = new AbortController();
|
|
7134
|
+
scan = controller;
|
|
7135
|
+
patch({ scanning: true });
|
|
7136
|
+
run(doc, controller);
|
|
7137
|
+
}
|
|
7138
|
+
/** Empties the hits now and scans again, after the debounce when `delay` is set. */
|
|
7139
|
+
function restart(delay) {
|
|
7140
|
+
reset();
|
|
7141
|
+
if (isBlankQuery(state.get().query)) return;
|
|
7142
|
+
patch({ scanning: true });
|
|
7143
|
+
if (delay) timer = setTimeout(start, debounceMs);
|
|
7144
|
+
else start();
|
|
7145
|
+
}
|
|
7146
|
+
function step(by) {
|
|
7147
|
+
const shown = currentHit !== void 0;
|
|
7148
|
+
flush(false);
|
|
7149
|
+
const { hits, current } = state.get();
|
|
7150
|
+
if (hits.length === 0) return;
|
|
7151
|
+
const index = shown ? (current + by + hits.length) % hits.length : current;
|
|
7152
|
+
const hit = hits[index];
|
|
7153
|
+
currentHit = hit;
|
|
7154
|
+
currentIndex = index;
|
|
7155
|
+
patch({ current: index });
|
|
7156
|
+
scrollTo(hit);
|
|
7157
|
+
}
|
|
7158
|
+
const stopDocument = opts.document.subscribe(() => {
|
|
7159
|
+
if (destroyed) return;
|
|
7160
|
+
reset();
|
|
7161
|
+
if (state.get().open) restart(false);
|
|
7162
|
+
});
|
|
7163
|
+
function setQuery(query) {
|
|
7164
|
+
patch({ query });
|
|
7165
|
+
restart(true);
|
|
7166
|
+
}
|
|
7167
|
+
function open(query) {
|
|
7168
|
+
const wasOpen = state.get().open;
|
|
7169
|
+
patch({ open: true });
|
|
7170
|
+
if (query !== void 0) setQuery(query);
|
|
7171
|
+
else if (!wasOpen && !isBlankQuery(state.get().query)) restart(false);
|
|
7172
|
+
}
|
|
7173
|
+
return {
|
|
7174
|
+
state: readonly(state),
|
|
7175
|
+
open,
|
|
7176
|
+
setQuery,
|
|
7177
|
+
setMatchCase(on) {
|
|
7178
|
+
patch({ matchCase: on });
|
|
7179
|
+
restart(false);
|
|
7180
|
+
},
|
|
7181
|
+
next: () => step(1),
|
|
7182
|
+
previous: () => step(-1),
|
|
7183
|
+
close() {
|
|
7184
|
+
reset();
|
|
7185
|
+
patch({ open: false });
|
|
7186
|
+
},
|
|
7187
|
+
attachBar() {
|
|
7188
|
+
let attached = true;
|
|
7189
|
+
bars.update((n) => n + 1);
|
|
7190
|
+
return () => {
|
|
7191
|
+
if (!attached) return;
|
|
7192
|
+
attached = false;
|
|
7193
|
+
bars.update((n) => n - 1);
|
|
7194
|
+
};
|
|
7195
|
+
},
|
|
7196
|
+
barAttached: {
|
|
7197
|
+
get: () => bars.get() > 0,
|
|
7198
|
+
subscribe(fn) {
|
|
7199
|
+
let last = bars.get() > 0;
|
|
7200
|
+
return bars.subscribe((n) => {
|
|
7201
|
+
if (n > 0 === last) return;
|
|
7202
|
+
last = n > 0;
|
|
7203
|
+
fn(last);
|
|
7204
|
+
});
|
|
7205
|
+
}
|
|
7206
|
+
},
|
|
7207
|
+
destroy() {
|
|
7208
|
+
destroyed = true;
|
|
7209
|
+
stopDocument();
|
|
7210
|
+
reset();
|
|
7211
|
+
}
|
|
7212
|
+
};
|
|
7213
|
+
}
|
|
7214
|
+
/**
|
|
7215
|
+
* The actions and state of `controller` without the bar registration: what callers of a viewer
|
|
7216
|
+
* or a plugin get.
|
|
7217
|
+
*
|
|
7218
|
+
* @internal Shared with the framework bindings.
|
|
7219
|
+
*/
|
|
7220
|
+
function viewerSearchOf(controller) {
|
|
7221
|
+
return {
|
|
7222
|
+
state: controller.state,
|
|
7223
|
+
open: (query) => controller.open(query),
|
|
7224
|
+
setQuery: (query) => controller.setQuery(query),
|
|
7225
|
+
setMatchCase: (on) => controller.setMatchCase(on),
|
|
7226
|
+
next: () => controller.next(),
|
|
7227
|
+
previous: () => controller.previous(),
|
|
7228
|
+
close: () => controller.close()
|
|
7229
|
+
};
|
|
7230
|
+
}
|
|
7231
|
+
//#endregion
|
|
7232
|
+
//#region src/dom/search/find-key.ts
|
|
7233
|
+
/** Longest page selection that becomes the query. */
|
|
7234
|
+
const MAX_SELECTION = 200;
|
|
7235
|
+
const FIELD = "[data-ink-find-bar] input";
|
|
7236
|
+
/**
|
|
7237
|
+
* The text selected on the page inside `root`, on one line and trimmed; '' when there is none,
|
|
7238
|
+
* when it is not in `root`, when it is in the find bar, or when it is too long to be a query.
|
|
7239
|
+
*/
|
|
7240
|
+
function pageSelectionText(root) {
|
|
7241
|
+
const sel = root.ownerDocument.defaultView?.getSelection();
|
|
7242
|
+
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return "";
|
|
7243
|
+
const { anchorNode, focusNode } = sel;
|
|
7244
|
+
if (!anchorNode || !focusNode || !root.contains(anchorNode) || !root.contains(focusNode)) return "";
|
|
7245
|
+
const bar = (node) => (node instanceof Element ? node : node.parentElement)?.closest("[data-ink-find-bar]") != null;
|
|
7246
|
+
if (bar(anchorNode) || bar(focusNode)) return "";
|
|
7247
|
+
const text = sel.toString().replace(/\s+/g, " ").trim();
|
|
7248
|
+
return text.length > MAX_SELECTION ? "" : text;
|
|
7249
|
+
}
|
|
7250
|
+
/**
|
|
7251
|
+
* F by its letter, or by its place on a layout without Latin letters (Cyrillic, Greek), where
|
|
7252
|
+
* `key` is that layout's letter. A Latin layout with another letter there keeps that letter.
|
|
7253
|
+
*/
|
|
7254
|
+
function isFindKey(e) {
|
|
7255
|
+
if (e.key.toLowerCase() === "f") return true;
|
|
7256
|
+
return e.code === "KeyF" && !/^[a-z]$/i.test(e.key);
|
|
7257
|
+
}
|
|
7258
|
+
/**
|
|
7259
|
+
* Ctrl/Cmd+F on a viewer's root. Call it from a `keydown` listener on that root (it reads
|
|
7260
|
+
* `currentTarget`). Takes the key only when a find bar is mounted for this viewer and the event
|
|
7261
|
+
* comes from inside the root; otherwise the browser keeps it. Opens the search with the page selection as
|
|
7262
|
+
* the query, if there is one, and focuses and selects the field. Returns whether it took the key.
|
|
7263
|
+
*
|
|
7264
|
+
* @internal Shared with the framework bindings.
|
|
7265
|
+
*/
|
|
7266
|
+
function handleFindKey(e, controller, selection) {
|
|
7267
|
+
if (!(e.ctrlKey || e.metaKey) || e.altKey || e.shiftKey || !isFindKey(e)) return false;
|
|
7268
|
+
if (!controller.barAttached.get()) return false;
|
|
7269
|
+
const root = e.currentTarget;
|
|
7270
|
+
if (!(root instanceof HTMLElement)) return false;
|
|
7271
|
+
if (!(e.target instanceof Node) || !root.contains(e.target)) return false;
|
|
7272
|
+
e.preventDefault();
|
|
7273
|
+
const text = selection();
|
|
7274
|
+
controller.open(text === "" ? void 0 : text);
|
|
7275
|
+
const field = root.querySelector(FIELD);
|
|
7276
|
+
field?.focus();
|
|
7277
|
+
field?.select();
|
|
7278
|
+
return true;
|
|
7279
|
+
}
|
|
7280
|
+
//#endregion
|
|
6873
7281
|
//#region src/dom/viewer/thread-popover.ts
|
|
6874
7282
|
function createThreadPopover(opts) {
|
|
6875
7283
|
const { root, store, activated, focusReturn } = opts;
|
|
@@ -7045,7 +7453,7 @@ const passThrough = {
|
|
|
7045
7453
|
react: () => noop
|
|
7046
7454
|
};
|
|
7047
7455
|
function createViewer(el, opts) {
|
|
7048
|
-
const { source: initial, assets, password, defaults, history, plugins, threads: withThreads = true, onReady, onError, onActivate, onBuild, onPageMount, onPageUnmount, onPageError, toolStyle: _toolStyle, toolOptions: _toolOptions, signatureLibrary: _signatureLibrary, signatures: signatureStore, ...view } = opts;
|
|
7456
|
+
const { source: initial, assets, password, defaults, history, plugins, threads: withThreads = true, onReady, onError, onActivate, onBuild, onPageMount, onPageUnmount, onPageError, toolStyle: _toolStyle, toolOptions: _toolOptions, signatureLibrary: _signatureLibrary, signatures: signatureStore, search: _search, ...view } = opts;
|
|
7049
7457
|
/** One library for the viewer's life, so templates and the menu outlive each document. */
|
|
7050
7458
|
const signatures = createSignatureLibrary(signatureStore ?? createMemorySignatureStore());
|
|
7051
7459
|
/** Tool options persist for the viewer's life; the tool style is per document. */
|
|
@@ -7077,6 +7485,16 @@ function createViewer(el, opts) {
|
|
|
7077
7485
|
root.append(viewportRoot);
|
|
7078
7486
|
el.append(root);
|
|
7079
7487
|
rootEl.set(scroll);
|
|
7488
|
+
/** One controller for the viewer's life: the host, the engine and the find bar share it. */
|
|
7489
|
+
const search = createSearchController({
|
|
7490
|
+
document: readonly(current),
|
|
7491
|
+
viewport: () => viewport.get()
|
|
7492
|
+
});
|
|
7493
|
+
/** What callers get: the actions, not the bar registration. */
|
|
7494
|
+
const viewerSearch = viewerSearchOf(search);
|
|
7495
|
+
root.addEventListener("keydown", (e) => {
|
|
7496
|
+
handleFindKey(e, search, () => pageSelectionText(root));
|
|
7497
|
+
});
|
|
7080
7498
|
/**
|
|
7081
7499
|
* One host for the viewer's life. It reads the history when a document opens, and `current`
|
|
7082
7500
|
* is set before it notifies, so that is the new document's. With no document open, a
|
|
@@ -7092,7 +7510,8 @@ function createViewer(el, opts) {
|
|
|
7092
7510
|
},
|
|
7093
7511
|
root: readonly(rootEl),
|
|
7094
7512
|
decorate: () => view.decorateAnnotation,
|
|
7095
|
-
viewport: () => viewport.get()
|
|
7513
|
+
viewport: () => viewport.get(),
|
|
7514
|
+
search: viewerSearch
|
|
7096
7515
|
});
|
|
7097
7516
|
const focusReturn = createFocusReturn({
|
|
7098
7517
|
root: () => root,
|
|
@@ -7144,6 +7563,7 @@ function createViewer(el, opts) {
|
|
|
7144
7563
|
const mounted = mountViewer({
|
|
7145
7564
|
...view,
|
|
7146
7565
|
plugins: host,
|
|
7566
|
+
search,
|
|
7147
7567
|
container: scroll,
|
|
7148
7568
|
document: doc,
|
|
7149
7569
|
toolOptions,
|
|
@@ -7194,6 +7614,7 @@ function createViewer(el, opts) {
|
|
|
7194
7614
|
}
|
|
7195
7615
|
let loads = 0;
|
|
7196
7616
|
let destroyed = false;
|
|
7617
|
+
const destroyers = [];
|
|
7197
7618
|
let stop = () => void 0;
|
|
7198
7619
|
/** Tears down the open document, if any, and destroys it. */
|
|
7199
7620
|
function close() {
|
|
@@ -7252,18 +7673,26 @@ function createViewer(el, opts) {
|
|
|
7252
7673
|
if (id === null || current.get()?.annotations.get(id)) activated.set(id);
|
|
7253
7674
|
},
|
|
7254
7675
|
commands: { get: (id) => host.command(id) },
|
|
7676
|
+
search: viewerSearch,
|
|
7255
7677
|
open,
|
|
7256
7678
|
destroy() {
|
|
7257
7679
|
if (destroyed) return;
|
|
7258
7680
|
destroyed = true;
|
|
7259
7681
|
loads++;
|
|
7260
7682
|
close();
|
|
7683
|
+
for (const fn of destroyers) fn();
|
|
7684
|
+
search.destroy();
|
|
7261
7685
|
host.destroy();
|
|
7262
7686
|
root.remove();
|
|
7263
7687
|
}
|
|
7264
7688
|
};
|
|
7265
7689
|
toolsByViewer.set(viewer, [...view.tools ?? [], ...host.tools]);
|
|
7266
7690
|
commandIdsByViewer.set(viewer, () => [...host.commands.keys()]);
|
|
7691
|
+
internals.set(viewer, {
|
|
7692
|
+
search,
|
|
7693
|
+
destroyers,
|
|
7694
|
+
destroyed: () => destroyed
|
|
7695
|
+
});
|
|
7267
7696
|
return viewer;
|
|
7268
7697
|
}
|
|
7269
7698
|
const toolsByViewer = /* @__PURE__ */ new WeakMap();
|
|
@@ -7276,11 +7705,29 @@ const commandIdsByViewer = /* @__PURE__ */ new WeakMap();
|
|
|
7276
7705
|
function hostCommandIdsOf(viewer) {
|
|
7277
7706
|
return commandIdsByViewer.get(viewer)?.() ?? [];
|
|
7278
7707
|
}
|
|
7708
|
+
const internals = /* @__PURE__ */ new WeakMap();
|
|
7709
|
+
/** @internal The viewer's search controller, which the find bar registers with. */
|
|
7710
|
+
function searchControllerOf(viewer) {
|
|
7711
|
+
const found = internals.get(viewer);
|
|
7712
|
+
if (!found) throw new Error("Not a viewer made by createViewer");
|
|
7713
|
+
return found.search;
|
|
7714
|
+
}
|
|
7715
|
+
/**
|
|
7716
|
+
* @internal Runs `fn` when the viewer is destroyed, for what a control set up on it; at once
|
|
7717
|
+
* when it already is.
|
|
7718
|
+
*/
|
|
7719
|
+
function onViewerDestroy(viewer, fn) {
|
|
7720
|
+
const found = internals.get(viewer);
|
|
7721
|
+
if (!found) return;
|
|
7722
|
+
if (found.destroyed()) fn();
|
|
7723
|
+
else found.destroyers.push(fn);
|
|
7724
|
+
}
|
|
7279
7725
|
//#endregion
|
|
7280
7726
|
//#region src/dom/viewer/controls.ts
|
|
7281
7727
|
/**
|
|
7282
7728
|
* Runs `connect` for every value `source` takes while it is set, undoing the previous run's
|
|
7283
|
-
* subscriptions first, and disables `control` while it is `null`.
|
|
7729
|
+
* subscriptions first, and disables `control` while it is `null`. Returns the function that
|
|
7730
|
+
* stops following.
|
|
7284
7731
|
*/
|
|
7285
7732
|
function follow(source, control, connect, reset) {
|
|
7286
7733
|
let stops = [];
|
|
@@ -7296,7 +7743,12 @@ function follow(source, control, connect, reset) {
|
|
|
7296
7743
|
else connect(value, watch);
|
|
7297
7744
|
};
|
|
7298
7745
|
run(source.get());
|
|
7299
|
-
source.subscribe(run);
|
|
7746
|
+
const stopSource = source.subscribe(run);
|
|
7747
|
+
return () => {
|
|
7748
|
+
stopSource();
|
|
7749
|
+
for (const s of stops) s();
|
|
7750
|
+
stops = [];
|
|
7751
|
+
};
|
|
7300
7752
|
}
|
|
7301
7753
|
const SVG = "http://www.w3.org/2000/svg";
|
|
7302
7754
|
const TOOL_ICONS = {
|
|
@@ -7331,6 +7783,7 @@ function createIcon(icon) {
|
|
|
7331
7783
|
}
|
|
7332
7784
|
return svg;
|
|
7333
7785
|
}
|
|
7786
|
+
/** @internal A labelled icon button, as the toolbar controls make them. */
|
|
7334
7787
|
function button(label, attrs, icon) {
|
|
7335
7788
|
const b = h("button", {
|
|
7336
7789
|
type: "button",
|
|
@@ -7342,6 +7795,31 @@ function button(label, attrs, icon) {
|
|
|
7342
7795
|
return b;
|
|
7343
7796
|
}
|
|
7344
7797
|
/**
|
|
7798
|
+
* Opens the viewer's find bar; pressed while it is open. Pressing it again focuses the field.
|
|
7799
|
+
* The bar itself is `createFindBar`.
|
|
7800
|
+
*/
|
|
7801
|
+
function createSearchButton(viewer) {
|
|
7802
|
+
const b = button("Find", {
|
|
7803
|
+
class: "ink-search-button",
|
|
7804
|
+
"data-ink-search-button": "",
|
|
7805
|
+
"aria-pressed": "false"
|
|
7806
|
+
}, Search);
|
|
7807
|
+
b.addEventListener("click", () => {
|
|
7808
|
+
viewer.search.open();
|
|
7809
|
+
const field = viewer.element.querySelector("[data-ink-find-bar] input");
|
|
7810
|
+
field?.focus();
|
|
7811
|
+
field?.select();
|
|
7812
|
+
});
|
|
7813
|
+
const show = (s) => {
|
|
7814
|
+
b.dataset.state = s.open ? "on" : "off";
|
|
7815
|
+
b.setAttribute("aria-pressed", String(s.open));
|
|
7816
|
+
};
|
|
7817
|
+
show(viewer.search.state.get());
|
|
7818
|
+
onViewerDestroy(viewer, viewer.search.state.subscribe(show));
|
|
7819
|
+
onViewerDestroy(viewer, follow(viewer.document, b, () => void 0));
|
|
7820
|
+
return b;
|
|
7821
|
+
}
|
|
7822
|
+
/**
|
|
7345
7823
|
* A toolbar following the WAI-ARIA toolbar pattern: its buttons share one Tab stop, with
|
|
7346
7824
|
* arrow keys, Home and End to move between them. Fields and selects are Tab stops of their
|
|
7347
7825
|
* own and keep their keys. Buttons added later join in.
|
|
@@ -7453,7 +7931,7 @@ function createZoomIn(viewer) {
|
|
|
7453
7931
|
"data-ink-zoom-in": ""
|
|
7454
7932
|
}, ZoomIn);
|
|
7455
7933
|
b.addEventListener("click", () => viewer.viewport.get()?.zoomBy(1.25));
|
|
7456
|
-
follow(viewer.viewport, b, () => void 0);
|
|
7934
|
+
onViewerDestroy(viewer, follow(viewer.viewport, b, () => void 0));
|
|
7457
7935
|
return b;
|
|
7458
7936
|
}
|
|
7459
7937
|
function createZoomOut(viewer) {
|
|
@@ -7462,7 +7940,7 @@ function createZoomOut(viewer) {
|
|
|
7462
7940
|
"data-ink-zoom-out": ""
|
|
7463
7941
|
}, ZoomOut);
|
|
7464
7942
|
b.addEventListener("click", () => viewer.viewport.get()?.zoomBy(.8));
|
|
7465
|
-
follow(viewer.viewport, b, () => void 0);
|
|
7943
|
+
onViewerDestroy(viewer, follow(viewer.viewport, b, () => void 0));
|
|
7466
7944
|
return b;
|
|
7467
7945
|
}
|
|
7468
7946
|
/** Fit width, fit page and the zoom levels; a zoom between levels gets an option of its own. */
|
|
@@ -7627,6 +8105,167 @@ function rovingTabindex(bar) {
|
|
|
7627
8105
|
};
|
|
7628
8106
|
}
|
|
7629
8107
|
//#endregion
|
|
8108
|
+
//#region src/dom/search/find-text.ts
|
|
8109
|
+
/**
|
|
8110
|
+
* The count beside the field: "3 of 17", "3 of 12…" while scanning, "No results".
|
|
8111
|
+
*
|
|
8112
|
+
* @internal Shared with the framework bindings.
|
|
8113
|
+
*/
|
|
8114
|
+
function findCountText(s) {
|
|
8115
|
+
if (isBlankQuery(s.query)) return "";
|
|
8116
|
+
if (s.hits.length === 0) return s.scanning ? "" : "No results";
|
|
8117
|
+
return `${s.current + 1} of ${s.hits.length}${s.scanning ? "…" : ""}`;
|
|
8118
|
+
}
|
|
8119
|
+
/**
|
|
8120
|
+
* Returns the function a bar calls with each search state for its live region's text.
|
|
8121
|
+
*
|
|
8122
|
+
* A settled scan reads "Match 3 of 17, page 2" or "No results". While a scan runs it says
|
|
8123
|
+
* nothing until a step moves the current hit, then "Match 3 of 12 so far, page 2", and keeps
|
|
8124
|
+
* that text as more hits arrive, so the region speaks once per step and once on settle rather
|
|
8125
|
+
* than once per hit.
|
|
8126
|
+
*
|
|
8127
|
+
* @internal Shared with the framework bindings; each bar keeps its own.
|
|
8128
|
+
*/
|
|
8129
|
+
function findAnnouncer() {
|
|
8130
|
+
let shown;
|
|
8131
|
+
let said = "";
|
|
8132
|
+
return (s) => {
|
|
8133
|
+
const hit = s.hits[s.current];
|
|
8134
|
+
if (!s.open || isBlankQuery(s.query)) {
|
|
8135
|
+
shown = void 0;
|
|
8136
|
+
said = "";
|
|
8137
|
+
} else if (!s.scanning) {
|
|
8138
|
+
shown = hit;
|
|
8139
|
+
said = hit ? `Match ${s.current + 1} of ${s.hits.length}, page ${hit.page + 1}` : "No results";
|
|
8140
|
+
} else {
|
|
8141
|
+
if (!hit) said = "";
|
|
8142
|
+
else if (shown && hit !== shown) said = `Match ${s.current + 1} of ${s.hits.length} so far, page ${hit.page + 1}`;
|
|
8143
|
+
shown = hit;
|
|
8144
|
+
}
|
|
8145
|
+
return said;
|
|
8146
|
+
};
|
|
8147
|
+
}
|
|
8148
|
+
//#endregion
|
|
8149
|
+
//#region src/dom/search/find-bar.ts
|
|
8150
|
+
/**
|
|
8151
|
+
* The find bar for `viewer`: a field, the match count, previous and next, a match-case toggle
|
|
8152
|
+
* and close. Its buttons keep focus while used; Enter in the field steps to the next match,
|
|
8153
|
+
* Shift+Enter to the previous, Escape closes. Closing returns focus to where it was before the
|
|
8154
|
+
* bar opened, else to the document.
|
|
8155
|
+
*/
|
|
8156
|
+
function createFindBar(viewer) {
|
|
8157
|
+
const controller = searchControllerOf(viewer);
|
|
8158
|
+
const { state } = controller;
|
|
8159
|
+
const field = h("input", {
|
|
8160
|
+
type: "text",
|
|
8161
|
+
"aria-label": "Find in document",
|
|
8162
|
+
autocomplete: "off",
|
|
8163
|
+
spellcheck: "false"
|
|
8164
|
+
});
|
|
8165
|
+
const count = h("span", {
|
|
8166
|
+
class: "ink-find-count",
|
|
8167
|
+
"data-ink-find-count": ""
|
|
8168
|
+
});
|
|
8169
|
+
const previous = button("Previous match", { "data-ink-find-previous": "" }, ChevronUp);
|
|
8170
|
+
const next = button("Next match", { "data-ink-find-next": "" }, ChevronDown);
|
|
8171
|
+
const matchCase = button("Match case", {
|
|
8172
|
+
"data-ink-find-match-case": "",
|
|
8173
|
+
"aria-pressed": "false"
|
|
8174
|
+
}, CaseSensitive);
|
|
8175
|
+
const close = button("Close find", { "data-ink-find-close": "" }, X);
|
|
8176
|
+
const live = h("div", {
|
|
8177
|
+
class: "ink-visually-hidden",
|
|
8178
|
+
"data-ink-find-status": "",
|
|
8179
|
+
"aria-live": "polite"
|
|
8180
|
+
});
|
|
8181
|
+
const bar = h("div", {
|
|
8182
|
+
class: "ink-find-bar",
|
|
8183
|
+
"data-ink-find-bar": "",
|
|
8184
|
+
role: "search",
|
|
8185
|
+
hidden: ""
|
|
8186
|
+
}, field, count, previous, next, matchCase, close, live);
|
|
8187
|
+
field.addEventListener("input", () => controller.setQuery(field.value));
|
|
8188
|
+
previous.addEventListener("click", () => controller.previous());
|
|
8189
|
+
next.addEventListener("click", () => controller.next());
|
|
8190
|
+
matchCase.addEventListener("click", () => controller.setMatchCase(!state.get().matchCase));
|
|
8191
|
+
close.addEventListener("click", () => controller.close());
|
|
8192
|
+
field.addEventListener("keydown", (e) => {
|
|
8193
|
+
if (e.key !== "Enter" || e.isComposing) return;
|
|
8194
|
+
e.preventDefault();
|
|
8195
|
+
if (e.shiftKey) controller.previous();
|
|
8196
|
+
else controller.next();
|
|
8197
|
+
});
|
|
8198
|
+
bar.addEventListener("keydown", (e) => {
|
|
8199
|
+
if (e.key !== "Escape" || e.defaultPrevented || e.isComposing) return;
|
|
8200
|
+
e.preventDefault();
|
|
8201
|
+
e.stopPropagation();
|
|
8202
|
+
controller.close();
|
|
8203
|
+
});
|
|
8204
|
+
/** Where focus was when the bar opened. */
|
|
8205
|
+
let before = null;
|
|
8206
|
+
let wasOpen = false;
|
|
8207
|
+
const announce = findAnnouncer();
|
|
8208
|
+
let announced = "";
|
|
8209
|
+
function restoreFocus() {
|
|
8210
|
+
const target = before?.isConnected && !bar.contains(before) ? before : viewer.element.querySelector("[data-ink-scroll]");
|
|
8211
|
+
before = null;
|
|
8212
|
+
target?.focus({ preventScroll: true });
|
|
8213
|
+
}
|
|
8214
|
+
function show(s) {
|
|
8215
|
+
if (s.open !== wasOpen) {
|
|
8216
|
+
wasOpen = s.open;
|
|
8217
|
+
if (s.open) {
|
|
8218
|
+
const active = bar.ownerDocument.activeElement;
|
|
8219
|
+
before = active instanceof HTMLElement && active !== bar.ownerDocument.body && !bar.contains(active) ? active : null;
|
|
8220
|
+
bar.hidden = false;
|
|
8221
|
+
field.focus();
|
|
8222
|
+
field.select();
|
|
8223
|
+
} else {
|
|
8224
|
+
const active = bar.ownerDocument.activeElement;
|
|
8225
|
+
const hadFocus = bar.contains(active) || active === bar.ownerDocument.body;
|
|
8226
|
+
bar.hidden = true;
|
|
8227
|
+
if (hadFocus) restoreFocus();
|
|
8228
|
+
else before = null;
|
|
8229
|
+
}
|
|
8230
|
+
}
|
|
8231
|
+
if (field.value !== s.query) {
|
|
8232
|
+
field.value = s.query;
|
|
8233
|
+
if (bar.ownerDocument.activeElement === field) field.select();
|
|
8234
|
+
}
|
|
8235
|
+
count.textContent = findCountText(s);
|
|
8236
|
+
matchCase.setAttribute("aria-pressed", String(s.matchCase));
|
|
8237
|
+
const say = announce(s);
|
|
8238
|
+
if (say !== announced) {
|
|
8239
|
+
announced = say;
|
|
8240
|
+
live.textContent = say;
|
|
8241
|
+
}
|
|
8242
|
+
}
|
|
8243
|
+
let detach = null;
|
|
8244
|
+
const sync = () => {
|
|
8245
|
+
const inside = viewer.element.contains(bar);
|
|
8246
|
+
if (inside && !detach) detach = controller.attachBar();
|
|
8247
|
+
else if (!inside && detach) {
|
|
8248
|
+
detach();
|
|
8249
|
+
detach = null;
|
|
8250
|
+
}
|
|
8251
|
+
};
|
|
8252
|
+
const observer = new MutationObserver(sync);
|
|
8253
|
+
observer.observe(viewer.element, {
|
|
8254
|
+
childList: true,
|
|
8255
|
+
subtree: true
|
|
8256
|
+
});
|
|
8257
|
+
const stopState = state.subscribe(show);
|
|
8258
|
+
show(state.get());
|
|
8259
|
+
sync();
|
|
8260
|
+
onViewerDestroy(viewer, () => {
|
|
8261
|
+
observer.disconnect();
|
|
8262
|
+
stopState();
|
|
8263
|
+
detach?.();
|
|
8264
|
+
detach = null;
|
|
8265
|
+
});
|
|
8266
|
+
return bar;
|
|
8267
|
+
}
|
|
8268
|
+
//#endregion
|
|
7630
8269
|
//#region src/dom/viewer/menu.ts
|
|
7631
8270
|
const FOCUSABLE = "select:not(:disabled), input:not(:disabled), button:not(:disabled):not([tabindex=\"-1\"])";
|
|
7632
8271
|
function createMenu(trigger, opts) {
|
|
@@ -9189,6 +9828,6 @@ function createSignatureMenu(viewer, opts = {}) {
|
|
|
9189
9828
|
return root;
|
|
9190
9829
|
}
|
|
9191
9830
|
//#endregion
|
|
9192
|
-
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, 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, TOOL_LABELS, ZOOM_LEVELS, annotationElementFor, arrowheadChoice, attachViewportKeys, builtinTools, clampFontSize, colorMenuModel, computeCssLayout, createColorMenu, createCommandButton, createFocusReturn, createHighlightMenu, createIcon, createImageSignature, createLineStyleMenu, createMenu, createNextPage, createOverlay, createPageCount, createPageInput, createPageMount, createPageRenderer, createPageTransform, createPluginHost, createPluginLayers, createPopover, createPrevPage, createRedo, createShapeMenu, createSignatureDialog, createSignatureDraft, createSignatureMenu, createSignaturePad, createSignaturePreview, createTextLayer, createTextStyleMenu, createToolButton, createToolController, createToolOptions, createToolbar, createToolbarSeparator, createTypedSignature, createUndo, createViewer, createViewport, createZoomIn, createZoomOut, createZoomSelect, cssRectsToUserRects, cssVariableDefaults, definePlugin, editFreeText, fitZoomCss, focusInitialMenuItem, fontFamilyValue, freetextTool, getSelectionQuads, handleMenuKeys, handleRadioKeys, handleTabKeys, highlightMenuModel, highlightTool, hitFor, lineStyleMenuModel, menuItems, mostVisiblePageCss, mountViewer, normalizeRotation, noteTool, observeAnnotationElement, placePanel, redactAreaTool, renderAnnotationContent, renderTypedSignature, selectTextTool, selectTool, shapeMenuModel, shapeTool, shouldKeepOpenOnAnnotation, signatureMenuModel, signaturePreview, signatureTool, textStyleMenuModel, threadDeleteLabel, threadPopoverLabel, trackToolStyle, trapTab, unknownCommand, visibleRangeCss, withHostTools };
|
|
9831
|
+
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, 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, TOOL_LABELS, ZOOM_LEVELS, annotationElementFor, arrowheadChoice, attachViewportKeys, builtinTools, clampFontSize, colorMenuModel, computeCssLayout, createColorMenu, createCommandButton, createFindBar, createFocusReturn, createHighlightMenu, createIcon, createImageSignature, createLineStyleMenu, createMenu, createNextPage, createOverlay, createPageCount, createPageInput, createPageMount, createPageRenderer, createPageTransform, createPluginHost, createPluginLayers, createPopover, createPrevPage, createRedo, createSearchButton, createSearchController, createShapeMenu, createSignatureDialog, createSignatureDraft, createSignatureMenu, createSignaturePad, createSignaturePreview, createTextLayer, createTextStyleMenu, createToolButton, createToolController, createToolOptions, createToolbar, createToolbarSeparator, createTypedSignature, createUndo, createViewer, createViewport, createZoomIn, createZoomOut, createZoomSelect, cssRectsToUserRects, cssVariableDefaults, definePlugin, editFreeText, findAnnouncer, findCountText, fitZoomCss, focusInitialMenuItem, fontFamilyValue, freetextTool, getSelectionQuads, handleFindKey, handleMenuKeys, handleRadioKeys, handleTabKeys, highlightMenuModel, highlightTool, hitFor, lineStyleMenuModel, menuItems, mostVisiblePageCss, mountViewer, normalizeRotation, noteTool, observeAnnotationElement, pageSelectionText, placePanel, redactAreaTool, renderAnnotationContent, renderTypedSignature, selectTextTool, selectTool, shapeMenuModel, shapeTool, shouldKeepOpenOnAnnotation, signatureMenuModel, signaturePreview, signatureTool, textStyleMenuModel, threadDeleteLabel, threadPopoverLabel, trackToolStyle, trapTab, unknownCommand, viewerSearchOf, visibleRangeCss, withHostTools };
|
|
9193
9832
|
|
|
9194
9833
|
//# sourceMappingURL=dom.mjs.map
|