@embedpdf/angular 3.0.0-next.13 → 3.0.0-next.15
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 +309 -0
- package/dist/README.md +309 -0
- package/dist/actions/index.d.ts +34 -16
- package/dist/anchored/index.d.ts +67 -0
- package/dist/annotation/index.d.ts +599 -0
- package/dist/commands/index.d.ts +109 -0
- package/dist/fesm2022/embedpdf-angular-actions.mjs +117 -36
- package/dist/fesm2022/embedpdf-angular-actions.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-anchored.mjs +148 -0
- package/dist/fesm2022/embedpdf-angular-anchored.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-annotation.mjs +1960 -0
- package/dist/fesm2022/embedpdf-angular-annotation.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-commands.mjs +235 -0
- package/dist/fesm2022/embedpdf-angular-commands.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-form.mjs +1074 -0
- package/dist/fesm2022/embedpdf-angular-form.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-history.mjs +54 -0
- package/dist/fesm2022/embedpdf-angular-history.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-i18n.mjs +100 -0
- package/dist/fesm2022/embedpdf-angular-i18n.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-interaction.mjs +157 -0
- package/dist/fesm2022/embedpdf-angular-interaction.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-link.mjs +306 -0
- package/dist/fesm2022/embedpdf-angular-link.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-measurement.mjs +106 -0
- package/dist/fesm2022/embedpdf-angular-measurement.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-metadata.mjs +77 -0
- package/dist/fesm2022/embedpdf-angular-metadata.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-page-edit.mjs +65 -0
- package/dist/fesm2022/embedpdf-angular-page-edit.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-page-view.mjs +297 -0
- package/dist/fesm2022/embedpdf-angular-page-view.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-redaction.mjs +93 -0
- package/dist/fesm2022/embedpdf-angular-redaction.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-render.mjs +331 -69
- package/dist/fesm2022/embedpdf-angular-render.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-runtime.mjs +1147 -390
- package/dist/fesm2022/embedpdf-angular-runtime.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-search.mjs +251 -0
- package/dist/fesm2022/embedpdf-angular-search.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-selection.mjs +477 -0
- package/dist/fesm2022/embedpdf-angular-selection.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-shell.mjs +177 -0
- package/dist/fesm2022/embedpdf-angular-shell.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-signature.mjs +120 -0
- package/dist/fesm2022/embedpdf-angular-signature.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-stage.mjs +688 -288
- package/dist/fesm2022/embedpdf-angular-stage.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-stamp.mjs +169 -0
- package/dist/fesm2022/embedpdf-angular-stamp.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-testing.mjs +89 -0
- package/dist/fesm2022/embedpdf-angular-testing.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-toolbar.mjs +1123 -0
- package/dist/fesm2022/embedpdf-angular-toolbar.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-view-manager.mjs +69 -0
- package/dist/fesm2022/embedpdf-angular-view-manager.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular.mjs +23 -3
- package/dist/fesm2022/embedpdf-angular.mjs.map +1 -1
- package/dist/form/index.d.ts +79 -0
- package/dist/history/index.d.ts +28 -0
- package/dist/i18n/index.d.ts +52 -0
- package/dist/index.d.ts +20 -0
- package/dist/interaction/index.d.ts +58 -0
- package/dist/link/index.d.ts +77 -0
- package/dist/measurement/index.d.ts +46 -0
- package/dist/metadata/index.d.ts +51 -0
- package/dist/page-edit/index.d.ts +22 -0
- package/dist/page-view/index.d.ts +94 -0
- package/dist/redaction/index.d.ts +38 -0
- package/dist/render/index.d.ts +45 -6
- package/dist/runtime/index.d.ts +579 -199
- package/dist/search/index.d.ts +71 -0
- package/dist/selection/index.d.ts +141 -0
- package/dist/shell/index.d.ts +88 -0
- package/dist/signature/index.d.ts +45 -0
- package/dist/stage/index.d.ts +242 -141
- package/dist/stamp/index.d.ts +58 -0
- package/dist/testing/index.d.ts +53 -0
- package/dist/toolbar/index.d.ts +257 -0
- package/dist/view-manager/index.d.ts +23 -0
- package/package.json +135 -14
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { selectionState, SelectionToken, selectionPlugin, HANDLE_PAD, HANDLE_BAR, HANDLE_HEAD, SELECTION_DEFAULTS, selectionHandleEndpointsOf, selectionHandleViewOf, armSelectionHandle, selectionHandleGeom } from '@embedpdf/plugin-selection';
|
|
2
|
+
export * from '@embedpdf/plugin-selection';
|
|
3
|
+
import { svgPoints, quadInPixels, paint, mixAccent, samePageBounds, attachSelectionHandle, sameSelectionEndpoints, wireSelectionClipboard } from '@embedpdf/web';
|
|
4
|
+
export { copySelection } from '@embedpdf/web';
|
|
5
|
+
import * as i0 from '@angular/core';
|
|
6
|
+
import { Injectable, computed, effect, untracked, ChangeDetectionStrategy, Component, input, inject, ElementRef, DestroyRef, Directive, signal, output, PLATFORM_ID } from '@angular/core';
|
|
7
|
+
import { pluginService, injectPage, injectKernelHost, CapabilityBinding, injectDocumentScope, devWarn } from '@embedpdf/angular/runtime';
|
|
8
|
+
import { SelectionToken as SelectionToken$1 } from '@embedpdf/plugin-selection/contract/host';
|
|
9
|
+
import { EpdfAnchored, EPDF_PROJECTOR } from '@embedpdf/angular/anchored';
|
|
10
|
+
import { StageToken } from '@embedpdf/plugin-stage/contract';
|
|
11
|
+
import { EpdfStage } from '@embedpdf/angular/stage';
|
|
12
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The selection plugin's service and feature:
|
|
16
|
+
*
|
|
17
|
+
* withSelection(options) the plugin, for provideEmbedPdf() (it needs withInteraction())
|
|
18
|
+
* inject(EpdfSelection) select from code, read the selected text, the State table as
|
|
19
|
+
* signals (`hasSelection()`, `isSelecting()`, `range()`, `pages()`),
|
|
20
|
+
* `changed$` / `committed$` / `cleared$`, and the settings
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Text selection: `select()` and the other methods of the Selecting page, every value of its
|
|
24
|
+
* State table as a signal (`selection.hasSelection()`), its events as streams
|
|
25
|
+
* (`selection.committed$`), and its settings (`selection.settings()`,
|
|
26
|
+
* `selection.updateSettings({ color })`). Without a document the signals read empty and the
|
|
27
|
+
* methods refuse with `not-ready`.
|
|
28
|
+
*/
|
|
29
|
+
class EpdfSelection extends pluginService({
|
|
30
|
+
name: 'EpdfSelection',
|
|
31
|
+
feature: 'withSelection()',
|
|
32
|
+
token: SelectionToken,
|
|
33
|
+
state: selectionState,
|
|
34
|
+
methods: [
|
|
35
|
+
'select',
|
|
36
|
+
'selectWordAt',
|
|
37
|
+
'selectLineAt',
|
|
38
|
+
'selectPage',
|
|
39
|
+
'selectAll',
|
|
40
|
+
'extendTo',
|
|
41
|
+
'clear',
|
|
42
|
+
'readText',
|
|
43
|
+
'readTextInRange',
|
|
44
|
+
'listSegments',
|
|
45
|
+
'listRects',
|
|
46
|
+
'getAnchor',
|
|
47
|
+
'getSnapshot',
|
|
48
|
+
'getRange',
|
|
49
|
+
'listSelectedPages',
|
|
50
|
+
'canSelect',
|
|
51
|
+
'canCopy',
|
|
52
|
+
],
|
|
53
|
+
events: ['onChanged', 'onCommitted', 'onCleared'],
|
|
54
|
+
}) {
|
|
55
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelection, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
56
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelection, providedIn: 'root' });
|
|
57
|
+
}
|
|
58
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelection, decorators: [{
|
|
59
|
+
type: Injectable,
|
|
60
|
+
args: [{ providedIn: 'root' }]
|
|
61
|
+
}] });
|
|
62
|
+
/**
|
|
63
|
+
* The selection plugin: selecting text with the pointer, a finger and from code. It works with
|
|
64
|
+
* the interaction plugin, so give `withInteraction()` too:
|
|
65
|
+
* `provideEmbedPdf({ engine }, withStage(), withInteraction(), withSelection())`.
|
|
66
|
+
*/
|
|
67
|
+
function withSelection(options) {
|
|
68
|
+
return { plugins: [selectionPlugin(options)], services: [EpdfSelection] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* `<epdf-selection-layer>`: the highlight of the selected text on one page. It only draws: the
|
|
73
|
+
* pointer goes to the interaction plugin (the Stage and the page view pass it on), and the
|
|
74
|
+
* selection plugin decides what is selected.
|
|
75
|
+
*
|
|
76
|
+
* Each selected line is a polygon of its four corners, placed through the page context, so
|
|
77
|
+
* turned text and turned pages are drawn exactly. The color is the plugin's `color` setting,
|
|
78
|
+
* the viewer's accent at 35% while it's unset, and the `--epdf-text-selection` CSS variable
|
|
79
|
+
* wins over both.
|
|
80
|
+
*/
|
|
81
|
+
const NO_SEGMENTS = Object.freeze([]);
|
|
82
|
+
class EpdfSelectionLayer {
|
|
83
|
+
page = injectPage('<epdf-selection-layer>');
|
|
84
|
+
host = injectKernelHost('<epdf-selection-layer>');
|
|
85
|
+
/** The selection plugin of the page's own document, with what layers need of it. */
|
|
86
|
+
selection = new CapabilityBinding(this.host, () => SelectionToken$1, () => this.page.documentId);
|
|
87
|
+
/** This page's selected lines: the same array until the selection on this page changes. */
|
|
88
|
+
segments = this.selection.select((selection) => selection.listSegments(this.page.ref), NO_SEGMENTS);
|
|
89
|
+
/** False while something else draws the selection itself (a highlighter's preview). */
|
|
90
|
+
visible = this.selection.select((selection) => selection.isHighlightVisible(), true);
|
|
91
|
+
color = this.selection.select((selection) => selection.getSettings().color, null);
|
|
92
|
+
/** Each line's corners in the page's pixels, which turn with the page. */
|
|
93
|
+
polygons = computed(() => {
|
|
94
|
+
const transform = this.page.transform();
|
|
95
|
+
return this.segments().map((segment) => svgPoints(quadInPixels(segment.quad, transform)));
|
|
96
|
+
}, ...(ngDevMode ? [{ debugName: "polygons" }] : []));
|
|
97
|
+
/** The CSS variable, then the setting, then the accent (with its own variable) at 35%. */
|
|
98
|
+
fill = computed(() => paint('text-selection', this.color() ?? mixAccent('text-selection', this.host.viewerSettings().accent)), ...(ngDevMode ? [{ debugName: "fill" }] : []));
|
|
99
|
+
constructor() {
|
|
100
|
+
// Read the page's text geometry as soon as the page is shown, so the first press can find
|
|
101
|
+
// the text under it at once. Without permission to select, nothing is read.
|
|
102
|
+
effect(() => {
|
|
103
|
+
const selection = this.selection.capability();
|
|
104
|
+
const page = this.page.ref;
|
|
105
|
+
if (selection)
|
|
106
|
+
untracked(() => void selection.ensureLoaded(page));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionLayer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
110
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfSelectionLayer, isStandalone: true, selector: "epdf-selection-layer", ngImport: i0, template: `
|
|
111
|
+
@if (visible() && polygons().length > 0) {
|
|
112
|
+
<svg
|
|
113
|
+
style="position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none"
|
|
114
|
+
>
|
|
115
|
+
@for (points of polygons(); track $index) {
|
|
116
|
+
<!-- The fill goes in style: an SVG attribute doesn't read var(). -->
|
|
117
|
+
<polygon [attr.points]="points" [style.fill]="fill()" />
|
|
118
|
+
}
|
|
119
|
+
</svg>
|
|
120
|
+
}
|
|
121
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
122
|
+
}
|
|
123
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionLayer, decorators: [{
|
|
124
|
+
type: Component,
|
|
125
|
+
args: [{
|
|
126
|
+
selector: 'epdf-selection-layer',
|
|
127
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
128
|
+
template: `
|
|
129
|
+
@if (visible() && polygons().length > 0) {
|
|
130
|
+
<svg
|
|
131
|
+
style="position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none"
|
|
132
|
+
>
|
|
133
|
+
@for (points of polygons(); track $index) {
|
|
134
|
+
<!-- The fill goes in style: an SVG attribute doesn't read var(). -->
|
|
135
|
+
<polygon [attr.points]="points" [style.fill]="fill()" />
|
|
136
|
+
}
|
|
137
|
+
</svg>
|
|
138
|
+
}
|
|
139
|
+
`,
|
|
140
|
+
}]
|
|
141
|
+
}], ctorParameters: () => [] });
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* `<epdf-selection-menu>`: your UI next to the selected text. Put it inside `<epdf-stage>`,
|
|
145
|
+
* next to the page template, with what the menu holds between its tags:
|
|
146
|
+
*
|
|
147
|
+
* <epdf-stage>
|
|
148
|
+
* <ng-template epdfPage>…</ng-template>
|
|
149
|
+
* <epdf-selection-menu placement="bottom">
|
|
150
|
+
* <button (click)="copy()">Copy</button>
|
|
151
|
+
* </epdf-selection-menu>
|
|
152
|
+
* </epdf-stage>
|
|
153
|
+
*
|
|
154
|
+
* It hides while the user drags and appears when the selection settles (a selection made from
|
|
155
|
+
* code shows at once). One menu for the whole selection: it sits by the selection on the page
|
|
156
|
+
* where it ends. It rides `<epdf-anchored>`, so it moves with the pages in the same frame, and a
|
|
157
|
+
* click in it never reaches the page.
|
|
158
|
+
*/
|
|
159
|
+
class EpdfSelectionMenu {
|
|
160
|
+
/** Which side of the selection: `'top'`, `'bottom'`, `'left'` or `'right'`, or a corner. */
|
|
161
|
+
placement = input('top', ...(ngDevMode ? [{ debugName: "placement" }] : []));
|
|
162
|
+
/** Screen pixels between the selection and the menu. */
|
|
163
|
+
gap = input(8, ...(ngDevMode ? [{ debugName: "gap" }] : []));
|
|
164
|
+
/** The selection of the document this part of the template talks to. */
|
|
165
|
+
selection = new CapabilityBinding(injectKernelHost('<epdf-selection-menu>'), () => SelectionToken, injectDocumentScope());
|
|
166
|
+
selecting = this.selection.select((selection) => selection.isSelecting(), false);
|
|
167
|
+
// A fresh object on every read: compared by value, so the menu moves only when the selection
|
|
168
|
+
// did.
|
|
169
|
+
settled = this.selection.select((selection) => selection.getAnchor(), null, samePageBounds);
|
|
170
|
+
/** Where the menu goes; nothing while a drag is still selecting. */
|
|
171
|
+
anchor = computed(() => (this.selecting() ? null : this.settled()), ...(ngDevMode ? [{ debugName: "anchor" }] : []));
|
|
172
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
173
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.4", type: EpdfSelectionMenu, isStandalone: true, selector: "epdf-selection-menu", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
174
|
+
<epdf-anchored [anchor]="anchor()" [placement]="placement()" [gap]="gap()">
|
|
175
|
+
<ng-content />
|
|
176
|
+
</epdf-anchored>
|
|
177
|
+
`, isInline: true, dependencies: [{ kind: "component", type: EpdfAnchored, selector: "epdf-anchored", inputs: ["anchor", "placement", "gap", "pinned"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
178
|
+
}
|
|
179
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionMenu, decorators: [{
|
|
180
|
+
type: Component,
|
|
181
|
+
args: [{
|
|
182
|
+
selector: 'epdf-selection-menu',
|
|
183
|
+
imports: [EpdfAnchored],
|
|
184
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
185
|
+
template: `
|
|
186
|
+
<epdf-anchored [anchor]="anchor()" [placement]="placement()" [gap]="gap()">
|
|
187
|
+
<ng-content />
|
|
188
|
+
</epdf-anchored>
|
|
189
|
+
`,
|
|
190
|
+
}]
|
|
191
|
+
}] });
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* `<epdf-selection-handles>`: the handles at both ends of the selection, as phones draw them, so
|
|
195
|
+
* a finger can grow or shrink it. Put it inside `<epdf-stage>`, next to the selection menu.
|
|
196
|
+
*
|
|
197
|
+
* Each handle is the selection's own edge, a thin bar the height of the text at that end (it
|
|
198
|
+
* turns with turned text and turned pages), with a round grip above the first line at the start
|
|
199
|
+
* and below the last line at the end. Dragging one extends the selection from the other end,
|
|
200
|
+
* through the same gesture a pointer drag uses, so the highlight, the menu and the events all
|
|
201
|
+
* behave the same. A press on a handle never scrolls the Stage.
|
|
202
|
+
*
|
|
203
|
+
* What's decided is the selection plugin's (`selectionHandleViewOf`, `selectionHandleEndpointsOf`,
|
|
204
|
+
* `selectionHandleGeom`, `armSelectionHandle`) and the listener mechanics are `@embedpdf/web`'s
|
|
205
|
+
* (`attachSelectionHandle`); this file keeps
|
|
206
|
+
* the markup and the colors: the plugin's `handles` setting, the accent while its color is
|
|
207
|
+
* unset, and the `--epdf-text-selection-handle` and `--epdf-text-selection-handle-shadow` CSS
|
|
208
|
+
* variables over both.
|
|
209
|
+
*/
|
|
210
|
+
const NO_HANDLES = Object.freeze([]);
|
|
211
|
+
/**
|
|
212
|
+
* Binds the drag to one handle element. `arm` is read when a press comes, so it always works
|
|
213
|
+
* with the selection as it is then.
|
|
214
|
+
*/
|
|
215
|
+
class EpdfSelectionGrip {
|
|
216
|
+
arm = input.required(...(ngDevMode ? [{ debugName: "arm", alias: 'epdfSelectionGrip' }] : [{
|
|
217
|
+
alias: 'epdfSelectionGrip',
|
|
218
|
+
}]));
|
|
219
|
+
constructor() {
|
|
220
|
+
const element = inject(ElementRef).nativeElement;
|
|
221
|
+
inject(DestroyRef).onDestroy(attachSelectionHandle(element, { arm: () => this.arm()() }));
|
|
222
|
+
}
|
|
223
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionGrip, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
224
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.2.4", type: EpdfSelectionGrip, isStandalone: true, selector: "[epdfSelectionGrip]", inputs: { arm: { classPropertyName: "arm", publicName: "epdfSelectionGrip", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
225
|
+
}
|
|
226
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionGrip, decorators: [{
|
|
227
|
+
type: Directive,
|
|
228
|
+
args: [{ selector: '[epdfSelectionGrip]' }]
|
|
229
|
+
}], ctorParameters: () => [] });
|
|
230
|
+
class EpdfSelectionHandles {
|
|
231
|
+
/** The view the handles are drawn over: the Stage they're in, unless you name another. */
|
|
232
|
+
token = input(undefined, ...(ngDevMode ? [{ debugName: "token" }] : []));
|
|
233
|
+
pad = HANDLE_PAD;
|
|
234
|
+
bar = HANDLE_BAR;
|
|
235
|
+
head = HANDLE_HEAD;
|
|
236
|
+
gripWidth = HANDLE_BAR + 2 * HANDLE_PAD;
|
|
237
|
+
host = injectKernelHost('<epdf-selection-handles>');
|
|
238
|
+
enclosingStage = inject(EpdfStage, { optional: true });
|
|
239
|
+
/** How the surface it's in places things: the Stage's camera, or nothing to project through. */
|
|
240
|
+
projection = inject(EPDF_PROJECTOR, { optional: true });
|
|
241
|
+
documentId = injectDocumentScope();
|
|
242
|
+
selection = new CapabilityBinding(this.host, () => SelectionToken$1, this.documentId);
|
|
243
|
+
stage = new CapabilityBinding(this.host, () => this.token() ?? this.enclosingStage?.token() ?? StageToken, this.documentId).capability;
|
|
244
|
+
selecting = this.selection.select((selection) => selection.isSelecting(), false);
|
|
245
|
+
visible = this.selection.select((selection) => selection.isHighlightVisible(), true);
|
|
246
|
+
settings = this.selection.select((selection) => selection.getSettings().handles, SELECTION_DEFAULTS.handles);
|
|
247
|
+
/** Both ends of the selection, compared corner by corner: the same until one moves. */
|
|
248
|
+
endpoints = this.selection.select((selection) => selectionHandleEndpointsOf(selection.getSnapshot()), null, sameSelectionEndpoints);
|
|
249
|
+
/** The handle being dragged: it keeps both handles shown while it selects. */
|
|
250
|
+
dragging = signal(null, ...(ngDevMode ? [{ debugName: "dragging" }] : []));
|
|
251
|
+
/** Each color is its CSS variable first, then the setting; an unset color is the accent. */
|
|
252
|
+
color = computed(() => paint('text-selection-handle', this.settings().color ?? this.host.viewerSettings().accent), ...(ngDevMode ? [{ debugName: "color" }] : []));
|
|
253
|
+
shadow = computed(() => paint('text-selection-handle-shadow', this.settings().shadow), ...(ngDevMode ? [{ debugName: "shadow" }] : []));
|
|
254
|
+
/** Whether this sits over a Stage, the only surface with a camera to place handles through. */
|
|
255
|
+
overStage = computed(() => this.projection?.projector()?.space === 'overlay', ...(ngDevMode ? [{ debugName: "overStage" }] : []));
|
|
256
|
+
handles = computed(() => {
|
|
257
|
+
const stage = this.stage();
|
|
258
|
+
const endpoints = this.endpoints();
|
|
259
|
+
if (!this.overStage() || !stage || !endpoints || !this.visible())
|
|
260
|
+
return NO_HANDLES;
|
|
261
|
+
// Hidden while a drag selects text, like the menu; a handle's own drag keeps them.
|
|
262
|
+
if (this.selecting() && !this.dragging())
|
|
263
|
+
return NO_HANDLES;
|
|
264
|
+
// The camera: the handles move in the same pass as the pages.
|
|
265
|
+
this.projection?.revision();
|
|
266
|
+
const view = selectionHandleViewOf(stage);
|
|
267
|
+
return ['start', 'end'].flatMap((role) => {
|
|
268
|
+
const layout = layoutOf(view, endpoints[role], role);
|
|
269
|
+
return layout ? [layout] : [];
|
|
270
|
+
});
|
|
271
|
+
}, ...(ngDevMode ? [{ debugName: "handles" }] : []));
|
|
272
|
+
armStart = () => this.arm('start');
|
|
273
|
+
armEnd = () => this.arm('end');
|
|
274
|
+
constructor() {
|
|
275
|
+
if (!this.projection)
|
|
276
|
+
warnOutsideStage();
|
|
277
|
+
effect(() => {
|
|
278
|
+
if (this.projection?.projector()?.space === 'client')
|
|
279
|
+
warnOutsideStage();
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/** A press on a handle: start a drag from the other end, with the selection as it is now. */
|
|
283
|
+
arm(role) {
|
|
284
|
+
const stage = untracked(this.stage);
|
|
285
|
+
const endpoints = untracked(this.endpoints);
|
|
286
|
+
const selection = untracked(this.selection.capability);
|
|
287
|
+
if (!stage || !endpoints || !selection)
|
|
288
|
+
return null;
|
|
289
|
+
const armed = armSelectionHandle(selection, selectionHandleViewOf(stage), endpoints, role);
|
|
290
|
+
if (!armed)
|
|
291
|
+
return null;
|
|
292
|
+
this.dragging.set(role);
|
|
293
|
+
return {
|
|
294
|
+
// The point the user grabbed: the middle of the bar.
|
|
295
|
+
base: armed.base,
|
|
296
|
+
session: {
|
|
297
|
+
move: armed.drag.move,
|
|
298
|
+
end: () => {
|
|
299
|
+
armed.drag.end(); // settles: the menu comes back, and committed$ fires
|
|
300
|
+
this.dragging.set(null);
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionHandles, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
306
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfSelectionHandles, isStandalone: true, selector: "epdf-selection-handles", inputs: { token: { classPropertyName: "token", publicName: "token", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
307
|
+
@for (handle of handles(); track handle.role) {
|
|
308
|
+
<div
|
|
309
|
+
style="position: absolute; touch-action: none; cursor: grab; pointer-events: auto"
|
|
310
|
+
[epdfSelectionGrip]="handle.role === 'start' ? armStart : armEnd"
|
|
311
|
+
[style.left.px]="handle.left"
|
|
312
|
+
[style.top.px]="handle.top"
|
|
313
|
+
[style.width.px]="gripWidth"
|
|
314
|
+
[style.height.px]="handle.height"
|
|
315
|
+
[style.transform]="handle.transform"
|
|
316
|
+
[style.transform-origin]="handle.transformOrigin"
|
|
317
|
+
>
|
|
318
|
+
<!-- The bar: the selection's own edge, as tall as the text at that end. -->
|
|
319
|
+
<div
|
|
320
|
+
style="position: absolute"
|
|
321
|
+
[style.left.px]="pad"
|
|
322
|
+
[style.top.px]="handle.barTop"
|
|
323
|
+
[style.width.px]="bar"
|
|
324
|
+
[style.height.px]="handle.barHeight"
|
|
325
|
+
[style.border-radius.px]="bar / 2"
|
|
326
|
+
[style.background]="color()"
|
|
327
|
+
></div>
|
|
328
|
+
<!-- The grip: against the bar, above the text at the start, below it at the end. -->
|
|
329
|
+
<div
|
|
330
|
+
style="position: absolute; border-radius: 50%"
|
|
331
|
+
[style.left.px]="pad + bar / 2 - head / 2"
|
|
332
|
+
[style.top.px]="handle.headTop"
|
|
333
|
+
[style.width.px]="head"
|
|
334
|
+
[style.height.px]="head"
|
|
335
|
+
[style.background]="color()"
|
|
336
|
+
[style.box-shadow]="shadow()"
|
|
337
|
+
></div>
|
|
338
|
+
</div>
|
|
339
|
+
}
|
|
340
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: EpdfSelectionGrip, selector: "[epdfSelectionGrip]", inputs: ["epdfSelectionGrip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
341
|
+
}
|
|
342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionHandles, decorators: [{
|
|
343
|
+
type: Component,
|
|
344
|
+
args: [{
|
|
345
|
+
selector: 'epdf-selection-handles',
|
|
346
|
+
imports: [EpdfSelectionGrip],
|
|
347
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
348
|
+
template: `
|
|
349
|
+
@for (handle of handles(); track handle.role) {
|
|
350
|
+
<div
|
|
351
|
+
style="position: absolute; touch-action: none; cursor: grab; pointer-events: auto"
|
|
352
|
+
[epdfSelectionGrip]="handle.role === 'start' ? armStart : armEnd"
|
|
353
|
+
[style.left.px]="handle.left"
|
|
354
|
+
[style.top.px]="handle.top"
|
|
355
|
+
[style.width.px]="gripWidth"
|
|
356
|
+
[style.height.px]="handle.height"
|
|
357
|
+
[style.transform]="handle.transform"
|
|
358
|
+
[style.transform-origin]="handle.transformOrigin"
|
|
359
|
+
>
|
|
360
|
+
<!-- The bar: the selection's own edge, as tall as the text at that end. -->
|
|
361
|
+
<div
|
|
362
|
+
style="position: absolute"
|
|
363
|
+
[style.left.px]="pad"
|
|
364
|
+
[style.top.px]="handle.barTop"
|
|
365
|
+
[style.width.px]="bar"
|
|
366
|
+
[style.height.px]="handle.barHeight"
|
|
367
|
+
[style.border-radius.px]="bar / 2"
|
|
368
|
+
[style.background]="color()"
|
|
369
|
+
></div>
|
|
370
|
+
<!-- The grip: against the bar, above the text at the start, below it at the end. -->
|
|
371
|
+
<div
|
|
372
|
+
style="position: absolute; border-radius: 50%"
|
|
373
|
+
[style.left.px]="pad + bar / 2 - head / 2"
|
|
374
|
+
[style.top.px]="handle.headTop"
|
|
375
|
+
[style.width.px]="head"
|
|
376
|
+
[style.height.px]="head"
|
|
377
|
+
[style.background]="color()"
|
|
378
|
+
[style.box-shadow]="shadow()"
|
|
379
|
+
></div>
|
|
380
|
+
</div>
|
|
381
|
+
}
|
|
382
|
+
`,
|
|
383
|
+
}]
|
|
384
|
+
}], ctorParameters: () => [] });
|
|
385
|
+
/**
|
|
386
|
+
* A handle laid out upright in its own box (the bar as long as the text is tall, the grip
|
|
387
|
+
* above it at the start or below it at the end), then turned onto the text's edge about the
|
|
388
|
+
* bar's top end: the one point that must sit exactly on the glyph's corner.
|
|
389
|
+
*/
|
|
390
|
+
function layoutOf(view, endpoint, role) {
|
|
391
|
+
const geometry = selectionHandleGeom(view, endpoint, role);
|
|
392
|
+
if (!geometry)
|
|
393
|
+
return null; // its page isn't laid out right now
|
|
394
|
+
const barTop = HANDLE_PAD + (role === 'start' ? HANDLE_HEAD : 0);
|
|
395
|
+
const pivotX = HANDLE_PAD + HANDLE_BAR / 2;
|
|
396
|
+
return {
|
|
397
|
+
role,
|
|
398
|
+
left: geometry.bar.from.x - pivotX,
|
|
399
|
+
top: geometry.bar.from.y - barTop,
|
|
400
|
+
height: geometry.length + HANDLE_HEAD + 2 * HANDLE_PAD,
|
|
401
|
+
barTop,
|
|
402
|
+
barHeight: geometry.length,
|
|
403
|
+
headTop: role === 'start' ? HANDLE_PAD : HANDLE_PAD + geometry.length,
|
|
404
|
+
transform: geometry.upright ? null : `rotate(${geometry.rotation}deg)`,
|
|
405
|
+
transformOrigin: geometry.upright ? null : `${pivotX}px ${barTop}px`,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function warnOutsideStage() {
|
|
409
|
+
devWarn('selection-handles-outside-stage', '<epdf-selection-handles> draws nothing here: put it inside <epdf-stage> ' +
|
|
410
|
+
'(an <epdf-page-view> has no camera to place the handles through).');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Ctrl+C, Cmd+C and the browser's Copy menu copy the selected text. It draws nothing:
|
|
415
|
+
*
|
|
416
|
+
* <epdf-selection-clipboard /> once next to a document view: anywhere on the page
|
|
417
|
+
* <div tabindex="0" epdfSelectionClipboard> only while focus is inside that element, for a
|
|
418
|
+
* page with more than one viewer
|
|
419
|
+
*
|
|
420
|
+
* It reads the selected text as soon as a selection settles, so the browser's copy event can be
|
|
421
|
+
* answered at once (it can't wait for a read), and copies with the Clipboard API when there's
|
|
422
|
+
* no page selection for the browser to copy. `[prefetch]="false"` reads the text only when
|
|
423
|
+
* someone copies. A selection the user may not copy isn't copied, and `(refused)` fires. The mechanics are `@embedpdf/web`'s, shared by every framework.
|
|
424
|
+
*/
|
|
425
|
+
class EpdfSelectionClipboard {
|
|
426
|
+
/** Read the selected text when the selection settles (default true). */
|
|
427
|
+
prefetch = input(undefined, ...(ngDevMode ? [{ debugName: "prefetch" }] : []));
|
|
428
|
+
/** Ctrl+C or the Copy menu met a selection the user may not copy: say so. */
|
|
429
|
+
refused = output();
|
|
430
|
+
selection = new CapabilityBinding(injectKernelHost('<epdf-selection-clipboard>'), () => SelectionToken, injectDocumentScope()).capability;
|
|
431
|
+
constructor() {
|
|
432
|
+
const host = inject(ElementRef).nativeElement;
|
|
433
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
434
|
+
return;
|
|
435
|
+
// Its own element copies anywhere on the page; another element, only from inside it.
|
|
436
|
+
const target = host.localName === 'epdf-selection-clipboard' ? undefined : host;
|
|
437
|
+
// Wired for the document's selection while there is one; again for the next document.
|
|
438
|
+
effect((onCleanup) => {
|
|
439
|
+
const selection = this.selection();
|
|
440
|
+
const prefetch = this.prefetch();
|
|
441
|
+
if (!selection)
|
|
442
|
+
return;
|
|
443
|
+
onCleanup(wireSelectionClipboard(selection, {
|
|
444
|
+
...(prefetch === undefined ? {} : { prefetch }),
|
|
445
|
+
...(target ? { target } : {}),
|
|
446
|
+
onRefused: () => this.refused.emit(),
|
|
447
|
+
}));
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionClipboard, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
451
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.2.4", type: EpdfSelectionClipboard, isStandalone: true, selector: "epdf-selection-clipboard, [epdfSelectionClipboard]", inputs: { prefetch: { classPropertyName: "prefetch", publicName: "prefetch", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refused: "refused" }, ngImport: i0 });
|
|
452
|
+
}
|
|
453
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfSelectionClipboard, decorators: [{
|
|
454
|
+
type: Directive,
|
|
455
|
+
args: [{ selector: 'epdf-selection-clipboard, [epdfSelectionClipboard]' }]
|
|
456
|
+
}], ctorParameters: () => [] });
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @embedpdf/angular/selection: selecting and copying text.
|
|
460
|
+
*
|
|
461
|
+
* withSelection(options) the plugin, for provideEmbedPdf() (with withInteraction())
|
|
462
|
+
* inject(EpdfSelection) select from code, read the text, the state, the events
|
|
463
|
+
* <epdf-selection-layer> the highlight, in each page
|
|
464
|
+
* <epdf-selection-menu> your menu next to the selection, inside <epdf-stage>
|
|
465
|
+
* <epdf-selection-handles> the handles a finger drags, inside <epdf-stage>
|
|
466
|
+
* <epdf-selection-clipboard> Ctrl+C and the browser's Copy menu
|
|
467
|
+
* <div epdfSelectionClipboard> the same, only while focus is inside that element
|
|
468
|
+
* copySelection(selection) the selected text to the clipboard, from your own button
|
|
469
|
+
*/
|
|
470
|
+
// The plugin's types and helpers, so app code has one import for the feature.
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Generated bundle index. Do not edit.
|
|
474
|
+
*/
|
|
475
|
+
|
|
476
|
+
export { EpdfSelection, EpdfSelectionClipboard, EpdfSelectionHandles, EpdfSelectionLayer, EpdfSelectionMenu, withSelection };
|
|
477
|
+
//# sourceMappingURL=embedpdf-angular-selection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedpdf-angular-selection.mjs","sources":["../../selection/src/selection.ts","../../selection/src/selection-layer.ts","../../selection/src/selection-menu.ts","../../selection/src/selection-handles.ts","../../selection/src/selection-clipboard.ts","../../selection/src/public_api.ts","../../selection/src/embedpdf-angular-selection.ts"],"sourcesContent":["/**\n * The selection plugin's service and feature:\n *\n * withSelection(options) the plugin, for provideEmbedPdf() (it needs withInteraction())\n * inject(EpdfSelection) select from code, read the selected text, the State table as\n * signals (`hasSelection()`, `isSelecting()`, `range()`, `pages()`),\n * `changed$` / `committed$` / `cleared$`, and the settings\n */\nimport { Injectable } from '@angular/core';\nimport { pluginService, type EmbedPdfFeature } from '@embedpdf/angular/runtime';\nimport {\n selectionPlugin,\n selectionState,\n SelectionToken,\n type SelectionConfig,\n} from '@embedpdf/plugin-selection';\n\n/**\n * Text selection: `select()` and the other methods of the Selecting page, every value of its\n * State table as a signal (`selection.hasSelection()`), its events as streams\n * (`selection.committed$`), and its settings (`selection.settings()`,\n * `selection.updateSettings({ color })`). Without a document the signals read empty and the\n * methods refuse with `not-ready`.\n */\n@Injectable({ providedIn: 'root' })\nexport class EpdfSelection extends pluginService({\n name: 'EpdfSelection',\n feature: 'withSelection()',\n token: SelectionToken,\n state: selectionState,\n methods: [\n 'select',\n 'selectWordAt',\n 'selectLineAt',\n 'selectPage',\n 'selectAll',\n 'extendTo',\n 'clear',\n 'readText',\n 'readTextInRange',\n 'listSegments',\n 'listRects',\n 'getAnchor',\n 'getSnapshot',\n 'getRange',\n 'listSelectedPages',\n 'canSelect',\n 'canCopy',\n ],\n events: ['onChanged', 'onCommitted', 'onCleared'],\n}) {}\n\n/**\n * The selection plugin: selecting text with the pointer, a finger and from code. It works with\n * the interaction plugin, so give `withInteraction()` too:\n * `provideEmbedPdf({ engine }, withStage(), withInteraction(), withSelection())`.\n */\nexport function withSelection(options?: SelectionConfig): EmbedPdfFeature {\n return { plugins: [selectionPlugin(options)], services: [EpdfSelection] };\n}\n","/**\n * `<epdf-selection-layer>`: the highlight of the selected text on one page. It only draws: the\n * pointer goes to the interaction plugin (the Stage and the page view pass it on), and the\n * selection plugin decides what is selected.\n *\n * Each selected line is a polygon of its four corners, placed through the page context, so\n * turned text and turned pages are drawn exactly. The color is the plugin's `color` setting,\n * the viewer's accent at 35% while it's unset, and the `--epdf-text-selection` CSS variable\n * wins over both.\n */\nimport { ChangeDetectionStrategy, Component, computed, effect, untracked } from '@angular/core';\nimport {\n SelectionToken as SelectionHostToken,\n type SelectionHostCapability,\n type SelectionSegment,\n} from '@embedpdf/plugin-selection/contract/host';\nimport { mixAccent, paint, quadInPixels, svgPoints } from '@embedpdf/web';\nimport { CapabilityBinding, injectKernelHost, injectPage } from '@embedpdf/angular/runtime';\n\nconst NO_SEGMENTS: readonly SelectionSegment[] = Object.freeze([]);\n\n@Component({\n selector: 'epdf-selection-layer',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (visible() && polygons().length > 0) {\n <svg\n style=\"position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none\"\n >\n @for (points of polygons(); track $index) {\n <!-- The fill goes in style: an SVG attribute doesn't read var(). -->\n <polygon [attr.points]=\"points\" [style.fill]=\"fill()\" />\n }\n </svg>\n }\n `,\n})\nexport class EpdfSelectionLayer {\n private readonly page = injectPage('<epdf-selection-layer>');\n private readonly host = injectKernelHost('<epdf-selection-layer>');\n /** The selection plugin of the page's own document, with what layers need of it. */\n private readonly selection = new CapabilityBinding<SelectionHostCapability>(\n this.host,\n () => SelectionHostToken,\n () => this.page.documentId,\n );\n\n /** This page's selected lines: the same array until the selection on this page changes. */\n private readonly segments = this.selection.select(\n (selection) => selection.listSegments(this.page.ref),\n NO_SEGMENTS,\n );\n /** False while something else draws the selection itself (a highlighter's preview). */\n protected readonly visible = this.selection.select(\n (selection) => selection.isHighlightVisible(),\n true,\n );\n private readonly color = this.selection.select(\n (selection) => selection.getSettings().color,\n null,\n );\n\n /** Each line's corners in the page's pixels, which turn with the page. */\n protected readonly polygons = computed(() => {\n const transform = this.page.transform();\n return this.segments().map((segment) => svgPoints(quadInPixels(segment.quad, transform)));\n });\n /** The CSS variable, then the setting, then the accent (with its own variable) at 35%. */\n protected readonly fill = computed(() =>\n paint(\n 'text-selection',\n this.color() ?? mixAccent('text-selection', this.host.viewerSettings().accent),\n ),\n );\n\n constructor() {\n // Read the page's text geometry as soon as the page is shown, so the first press can find\n // the text under it at once. Without permission to select, nothing is read.\n effect(() => {\n const selection = this.selection.capability();\n const page = this.page.ref;\n if (selection) untracked(() => void selection.ensureLoaded(page));\n });\n }\n}\n","/**\n * `<epdf-selection-menu>`: your UI next to the selected text. Put it inside `<epdf-stage>`,\n * next to the page template, with what the menu holds between its tags:\n *\n * <epdf-stage>\n * <ng-template epdfPage>…</ng-template>\n * <epdf-selection-menu placement=\"bottom\">\n * <button (click)=\"copy()\">Copy</button>\n * </epdf-selection-menu>\n * </epdf-stage>\n *\n * It hides while the user drags and appears when the selection settles (a selection made from\n * code shows at once). One menu for the whole selection: it sits by the selection on the page\n * where it ends. It rides `<epdf-anchored>`, so it moves with the pages in the same frame, and a\n * click in it never reaches the page.\n */\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { samePageBounds } from '@embedpdf/web';\nimport {\n CapabilityBinding,\n injectDocumentScope,\n injectKernelHost,\n} from '@embedpdf/angular/runtime';\nimport { EpdfAnchored, type AnchoredPlacement } from '@embedpdf/angular/anchored';\n\n@Component({\n selector: 'epdf-selection-menu',\n imports: [EpdfAnchored],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <epdf-anchored [anchor]=\"anchor()\" [placement]=\"placement()\" [gap]=\"gap()\">\n <ng-content />\n </epdf-anchored>\n `,\n})\nexport class EpdfSelectionMenu {\n /** Which side of the selection: `'top'`, `'bottom'`, `'left'` or `'right'`, or a corner. */\n readonly placement = input<AnchoredPlacement>('top');\n /** Screen pixels between the selection and the menu. */\n readonly gap = input(8);\n\n /** The selection of the document this part of the template talks to. */\n private readonly selection = new CapabilityBinding(\n injectKernelHost('<epdf-selection-menu>'),\n () => SelectionToken,\n injectDocumentScope(),\n );\n private readonly selecting = this.selection.select((selection) => selection.isSelecting(), false);\n // A fresh object on every read: compared by value, so the menu moves only when the selection\n // did.\n private readonly settled = this.selection.select(\n (selection) => selection.getAnchor(),\n null,\n samePageBounds,\n );\n /** Where the menu goes; nothing while a drag is still selecting. */\n protected readonly anchor = computed(() => (this.selecting() ? null : this.settled()));\n}\n","/**\n * `<epdf-selection-handles>`: the handles at both ends of the selection, as phones draw them, so\n * a finger can grow or shrink it. Put it inside `<epdf-stage>`, next to the selection menu.\n *\n * Each handle is the selection's own edge, a thin bar the height of the text at that end (it\n * turns with turned text and turned pages), with a round grip above the first line at the start\n * and below the last line at the end. Dragging one extends the selection from the other end,\n * through the same gesture a pointer drag uses, so the highlight, the menu and the events all\n * behave the same. A press on a handle never scrolls the Stage.\n *\n * What's decided is the selection plugin's (`selectionHandleViewOf`, `selectionHandleEndpointsOf`,\n * `selectionHandleGeom`, `armSelectionHandle`) and the listener mechanics are `@embedpdf/web`'s\n * (`attachSelectionHandle`); this file keeps\n * the markup and the colors: the plugin's `handles` setting, the accent while its color is\n * unset, and the `--epdf-text-selection-handle` and `--epdf-text-selection-handle-shadow` CSS\n * variables over both.\n */\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport {\n armSelectionHandle,\n HANDLE_BAR,\n HANDLE_HEAD,\n HANDLE_PAD,\n SELECTION_DEFAULTS,\n selectionHandleEndpointsOf,\n selectionHandleGeom,\n selectionHandleViewOf,\n type SelectionHandleEndpoint,\n type SelectionHandleView,\n} from '@embedpdf/plugin-selection';\nimport {\n SelectionToken as SelectionHostToken,\n type SelectionHostCapability,\n} from '@embedpdf/plugin-selection/contract/host';\nimport { StageToken, type StageCapability } from '@embedpdf/plugin-stage/contract';\nimport { attachSelectionHandle, paint, sameSelectionEndpoints } from '@embedpdf/web';\nimport type { SelectionHandleSession } from '@embedpdf/web';\nimport {\n CapabilityBinding,\n devWarn,\n injectDocumentScope,\n injectKernelHost,\n} from '@embedpdf/angular/runtime';\nimport { EPDF_PROJECTOR } from '@embedpdf/angular/anchored';\nimport { EpdfStage } from '@embedpdf/angular/stage';\n\ntype Role = 'start' | 'end';\n\n/** One handle, laid out in the Stage's own box. */\ninterface HandleLayout {\n readonly role: Role;\n readonly left: number;\n readonly top: number;\n readonly height: number;\n readonly barTop: number;\n readonly barHeight: number;\n readonly headTop: number;\n /** Null for upright text: an upright handle carries no transform. */\n readonly transform: string | null;\n readonly transformOrigin: string | null;\n}\n\nconst NO_HANDLES: readonly HandleLayout[] = Object.freeze([]);\n\n/** What a press on a handle starts: the point it grabbed (the bar's middle), and the drag. */\nexport type SelectionGripDrag = {\n readonly base: { readonly x: number; readonly y: number };\n readonly session: SelectionHandleSession;\n} | null;\n\n/**\n * Binds the drag to one handle element. `arm` is read when a press comes, so it always works\n * with the selection as it is then.\n */\n@Directive({ selector: '[epdfSelectionGrip]' })\nexport class EpdfSelectionGrip {\n readonly arm = input.required<() => SelectionGripDrag>({\n alias: 'epdfSelectionGrip',\n });\n\n constructor() {\n const element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n inject(DestroyRef).onDestroy(attachSelectionHandle(element, { arm: () => this.arm()() }));\n }\n}\n\n@Component({\n selector: 'epdf-selection-handles',\n imports: [EpdfSelectionGrip],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @for (handle of handles(); track handle.role) {\n <div\n style=\"position: absolute; touch-action: none; cursor: grab; pointer-events: auto\"\n [epdfSelectionGrip]=\"handle.role === 'start' ? armStart : armEnd\"\n [style.left.px]=\"handle.left\"\n [style.top.px]=\"handle.top\"\n [style.width.px]=\"gripWidth\"\n [style.height.px]=\"handle.height\"\n [style.transform]=\"handle.transform\"\n [style.transform-origin]=\"handle.transformOrigin\"\n >\n <!-- The bar: the selection's own edge, as tall as the text at that end. -->\n <div\n style=\"position: absolute\"\n [style.left.px]=\"pad\"\n [style.top.px]=\"handle.barTop\"\n [style.width.px]=\"bar\"\n [style.height.px]=\"handle.barHeight\"\n [style.border-radius.px]=\"bar / 2\"\n [style.background]=\"color()\"\n ></div>\n <!-- The grip: against the bar, above the text at the start, below it at the end. -->\n <div\n style=\"position: absolute; border-radius: 50%\"\n [style.left.px]=\"pad + bar / 2 - head / 2\"\n [style.top.px]=\"handle.headTop\"\n [style.width.px]=\"head\"\n [style.height.px]=\"head\"\n [style.background]=\"color()\"\n [style.box-shadow]=\"shadow()\"\n ></div>\n </div>\n }\n `,\n})\nexport class EpdfSelectionHandles {\n /** The view the handles are drawn over: the Stage they're in, unless you name another. */\n readonly token = input<CapabilityToken<StageCapability> | undefined>(undefined);\n\n protected readonly pad = HANDLE_PAD;\n protected readonly bar = HANDLE_BAR;\n protected readonly head = HANDLE_HEAD;\n protected readonly gripWidth = HANDLE_BAR + 2 * HANDLE_PAD;\n\n private readonly host = injectKernelHost('<epdf-selection-handles>');\n private readonly enclosingStage = inject(EpdfStage, { optional: true });\n /** How the surface it's in places things: the Stage's camera, or nothing to project through. */\n private readonly projection = inject(EPDF_PROJECTOR, { optional: true });\n private readonly documentId = injectDocumentScope();\n\n private readonly selection = new CapabilityBinding<SelectionHostCapability>(\n this.host,\n () => SelectionHostToken,\n this.documentId,\n );\n private readonly stage = new CapabilityBinding<StageCapability>(\n this.host,\n () => this.token() ?? this.enclosingStage?.token() ?? StageToken,\n this.documentId,\n ).capability;\n\n private readonly selecting = this.selection.select((selection) => selection.isSelecting(), false);\n private readonly visible = this.selection.select(\n (selection) => selection.isHighlightVisible(),\n true,\n );\n private readonly settings = this.selection.select(\n (selection) => selection.getSettings().handles,\n SELECTION_DEFAULTS.handles,\n );\n /** Both ends of the selection, compared corner by corner: the same until one moves. */\n private readonly endpoints = this.selection.select(\n (selection) => selectionHandleEndpointsOf(selection.getSnapshot()),\n null,\n sameSelectionEndpoints,\n );\n /** The handle being dragged: it keeps both handles shown while it selects. */\n private readonly dragging = signal<Role | null>(null);\n\n /** Each color is its CSS variable first, then the setting; an unset color is the accent. */\n protected readonly color = computed(() =>\n paint('text-selection-handle', this.settings().color ?? this.host.viewerSettings().accent),\n );\n protected readonly shadow = computed(() =>\n paint('text-selection-handle-shadow', this.settings().shadow),\n );\n\n /** Whether this sits over a Stage, the only surface with a camera to place handles through. */\n private readonly overStage = computed(() => this.projection?.projector()?.space === 'overlay');\n\n protected readonly handles = computed((): readonly HandleLayout[] => {\n const stage = this.stage();\n const endpoints = this.endpoints();\n if (!this.overStage() || !stage || !endpoints || !this.visible()) return NO_HANDLES;\n // Hidden while a drag selects text, like the menu; a handle's own drag keeps them.\n if (this.selecting() && !this.dragging()) return NO_HANDLES;\n // The camera: the handles move in the same pass as the pages.\n this.projection?.revision();\n const view = selectionHandleViewOf(stage);\n return (['start', 'end'] as const).flatMap((role) => {\n const layout = layoutOf(view, endpoints[role], role);\n return layout ? [layout] : [];\n });\n });\n\n protected readonly armStart = () => this.arm('start');\n protected readonly armEnd = () => this.arm('end');\n\n constructor() {\n if (!this.projection) warnOutsideStage();\n effect(() => {\n if (this.projection?.projector()?.space === 'client') warnOutsideStage();\n });\n }\n\n /** A press on a handle: start a drag from the other end, with the selection as it is now. */\n private arm(role: Role): SelectionGripDrag {\n const stage = untracked(this.stage);\n const endpoints = untracked(this.endpoints);\n const selection = untracked(this.selection.capability);\n if (!stage || !endpoints || !selection) return null;\n const armed = armSelectionHandle(selection, selectionHandleViewOf(stage), endpoints, role);\n if (!armed) return null;\n this.dragging.set(role);\n return {\n // The point the user grabbed: the middle of the bar.\n base: armed.base,\n session: {\n move: armed.drag.move,\n end: () => {\n armed.drag.end(); // settles: the menu comes back, and committed$ fires\n this.dragging.set(null);\n },\n },\n };\n }\n}\n\n/**\n * A handle laid out upright in its own box (the bar as long as the text is tall, the grip\n * above it at the start or below it at the end), then turned onto the text's edge about the\n * bar's top end: the one point that must sit exactly on the glyph's corner.\n */\nfunction layoutOf(\n view: SelectionHandleView,\n endpoint: SelectionHandleEndpoint,\n role: Role,\n): HandleLayout | null {\n const geometry = selectionHandleGeom(view, endpoint, role);\n if (!geometry) return null; // its page isn't laid out right now\n const barTop = HANDLE_PAD + (role === 'start' ? HANDLE_HEAD : 0);\n const pivotX = HANDLE_PAD + HANDLE_BAR / 2;\n return {\n role,\n left: geometry.bar.from.x - pivotX,\n top: geometry.bar.from.y - barTop,\n height: geometry.length + HANDLE_HEAD + 2 * HANDLE_PAD,\n barTop,\n barHeight: geometry.length,\n headTop: role === 'start' ? HANDLE_PAD : HANDLE_PAD + geometry.length,\n transform: geometry.upright ? null : `rotate(${geometry.rotation}deg)`,\n transformOrigin: geometry.upright ? null : `${pivotX}px ${barTop}px`,\n };\n}\n\nfunction warnOutsideStage(): void {\n devWarn(\n 'selection-handles-outside-stage',\n '<epdf-selection-handles> draws nothing here: put it inside <epdf-stage> ' +\n '(an <epdf-page-view> has no camera to place the handles through).',\n );\n}\n","/**\n * Ctrl+C, Cmd+C and the browser's Copy menu copy the selected text. It draws nothing:\n *\n * <epdf-selection-clipboard /> once next to a document view: anywhere on the page\n * <div tabindex=\"0\" epdfSelectionClipboard> only while focus is inside that element, for a\n * page with more than one viewer\n *\n * It reads the selected text as soon as a selection settles, so the browser's copy event can be\n * answered at once (it can't wait for a read), and copies with the Clipboard API when there's\n * no page selection for the browser to copy. `[prefetch]=\"false\"` reads the text only when\n * someone copies. A selection the user may not copy isn't copied, and `(refused)` fires. The mechanics are `@embedpdf/web`'s, shared by every framework.\n */\nimport { isPlatformBrowser } from '@angular/common';\nimport { Directive, effect, ElementRef, inject, input, output, PLATFORM_ID } from '@angular/core';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { wireSelectionClipboard } from '@embedpdf/web';\nimport {\n CapabilityBinding,\n injectDocumentScope,\n injectKernelHost,\n} from '@embedpdf/angular/runtime';\n\n@Directive({ selector: 'epdf-selection-clipboard, [epdfSelectionClipboard]' })\nexport class EpdfSelectionClipboard {\n /** Read the selected text when the selection settles (default true). */\n readonly prefetch = input<boolean | undefined>(undefined);\n /** Ctrl+C or the Copy menu met a selection the user may not copy: say so. */\n readonly refused = output<void>();\n\n private readonly selection = new CapabilityBinding(\n injectKernelHost('<epdf-selection-clipboard>'),\n () => SelectionToken,\n injectDocumentScope(),\n ).capability;\n\n constructor() {\n const host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n if (!isPlatformBrowser(inject(PLATFORM_ID))) return;\n // Its own element copies anywhere on the page; another element, only from inside it.\n const target = host.localName === 'epdf-selection-clipboard' ? undefined : host;\n // Wired for the document's selection while there is one; again for the next document.\n effect((onCleanup) => {\n const selection = this.selection();\n const prefetch = this.prefetch();\n if (!selection) return;\n onCleanup(\n wireSelectionClipboard(selection, {\n ...(prefetch === undefined ? {} : { prefetch }),\n ...(target ? { target } : {}),\n onRefused: () => this.refused.emit(),\n }),\n );\n });\n }\n}\n","/**\n * @embedpdf/angular/selection: selecting and copying text.\n *\n * withSelection(options) the plugin, for provideEmbedPdf() (with withInteraction())\n * inject(EpdfSelection) select from code, read the text, the state, the events\n * <epdf-selection-layer> the highlight, in each page\n * <epdf-selection-menu> your menu next to the selection, inside <epdf-stage>\n * <epdf-selection-handles> the handles a finger drags, inside <epdf-stage>\n * <epdf-selection-clipboard> Ctrl+C and the browser's Copy menu\n * <div epdfSelectionClipboard> the same, only while focus is inside that element\n * copySelection(selection) the selected text to the clipboard, from your own button\n */\n\n// The plugin's types and helpers, so app code has one import for the feature.\nexport * from '@embedpdf/plugin-selection';\n// The clipboard write lives in @embedpdf/web (the plugin never touches the DOM); re-exported so\n// the whole feature is one import.\nexport { copySelection } from '@embedpdf/web';\nexport * from './selection';\nexport { EpdfSelectionLayer } from './selection-layer';\nexport { EpdfSelectionMenu } from './selection-menu';\nexport { EpdfSelectionHandles } from './selection-handles';\nexport { EpdfSelectionClipboard } from './selection-clipboard';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":["SelectionHostToken"],"mappings":"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"}
|