@embedpdf/angular 3.0.0-next.14 → 3.0.0-next.16
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 +602 -0
- package/dist/attachment/index.d.ts +29 -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 +1933 -0
- package/dist/fesm2022/embedpdf-angular-annotation.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-attachment.mjs +67 -0
- package/dist/fesm2022/embedpdf-angular-attachment.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-outline.mjs +76 -0
- package/dist/fesm2022/embedpdf-angular-outline.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-page-edit.mjs +68 -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 +1151 -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 +25 -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 +22 -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/outline/index.d.ts +32 -0
- package/dist/page-edit/index.d.ts +27 -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 +145 -14
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
import { groupMenuView, normalizeBar, layoutToolbar } from '@embedpdf/core-ui';
|
|
2
|
+
export { DEFAULT_IMPORTANCE, PINNED, custom, group, item } from '@embedpdf/core-ui';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, TemplateRef, Directive, input, output, ChangeDetectionStrategy, Component, computed, viewChild, ElementRef, afterRenderEffect, untracked, signal, InjectionToken, PLATFORM_ID, effect, contentChild, contentChildren, forwardRef } from '@angular/core';
|
|
5
|
+
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
6
|
+
import { injectKernelHost, CapabilityBinding, injectDocumentScope } from '@embedpdf/angular/runtime';
|
|
7
|
+
import { EpdfCommands } from '@embedpdf/angular/commands';
|
|
8
|
+
import { unregisteredCommand } from '@embedpdf/plugin-commands/contract';
|
|
9
|
+
import { CommandsToken } from '@embedpdf/plugin-commands/contract/host';
|
|
10
|
+
import { paintDefault, focusablesIn, observeOutsidePress, attachMenuKeys, observeLength, createToolbarLengths, toolbarMeasureKey, observeContentLength, attachToolbarFocus } from '@embedpdf/web';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The parts of `<epdf-toolbar>` you draw, each an `<ng-template>` inside its tags, typed so
|
|
14
|
+
* `let-` gives what the part needs:
|
|
15
|
+
*
|
|
16
|
+
* <ng-template epdfToolbarCommand let-command let-variant="variant" let-run="run">
|
|
17
|
+
* <ng-template epdfToolbarCustom="page-number" let-variant>
|
|
18
|
+
* <ng-template epdfToolbarCollapsed let-view> a folded group
|
|
19
|
+
* <ng-template epdfToolbarGroupTrigger let-view> the button behind which a group sheds items
|
|
20
|
+
* <ng-template epdfToolbarSeparator> the line between groups
|
|
21
|
+
* <ng-template epdfToolbarOverflowTrigger let-open let-toggle="toggle"> the "More" button
|
|
22
|
+
* <ng-template epdfToolbarOverflowMenu let-view> the "More" menu
|
|
23
|
+
*
|
|
24
|
+
* A part you leave out has a plain default. The names carry `Toolbar` because `epdfCommand`
|
|
25
|
+
* is the directive for a button of your own.
|
|
26
|
+
*/
|
|
27
|
+
// ── the marker directives ───────────────────────────────────────────────────
|
|
28
|
+
/** A command's button. Without it: a plain `<button>` with the command's label. */
|
|
29
|
+
class EpdfToolbarCommandTemplate {
|
|
30
|
+
template = inject(TemplateRef);
|
|
31
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCommandTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
35
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfToolbarCommandTemplate, isStandalone: true, selector: "ng-template[epdfToolbarCommand]", ngImport: i0 });
|
|
36
|
+
}
|
|
37
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCommandTemplate, decorators: [{
|
|
38
|
+
type: Directive,
|
|
39
|
+
args: [{ selector: 'ng-template[epdfToolbarCommand]' }]
|
|
40
|
+
}] });
|
|
41
|
+
/**
|
|
42
|
+
* An item of your own, by the name `custom(name, command)` gave it in the bar. Without one, the
|
|
43
|
+
* item is a `<slot name>`: inside a shadow root your element's children of that `slot` go
|
|
44
|
+
* there; elsewhere it shows its command's button. The first element of the item that takes
|
|
45
|
+
* focus is its stop in the toolbar's arrow-key order.
|
|
46
|
+
*/
|
|
47
|
+
class EpdfToolbarCustomTemplate {
|
|
48
|
+
/** The item's name in the bar. */
|
|
49
|
+
slot = input.required(...(ngDevMode ? [{ debugName: "slot", alias: 'epdfToolbarCustom' }] : [{ alias: 'epdfToolbarCustom' }]));
|
|
50
|
+
template = inject(TemplateRef);
|
|
51
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCustomTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
55
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.2.4", type: EpdfToolbarCustomTemplate, isStandalone: true, selector: "ng-template[epdfToolbarCustom]", inputs: { slot: { classPropertyName: "slot", publicName: "epdfToolbarCustom", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
56
|
+
}
|
|
57
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCustomTemplate, decorators: [{
|
|
58
|
+
type: Directive,
|
|
59
|
+
args: [{ selector: 'ng-template[epdfToolbarCustom]' }]
|
|
60
|
+
}] });
|
|
61
|
+
/** A folded group. Without it: a `<select>` for `collapse: 'select'`, a menu button for `'menu'`. */
|
|
62
|
+
class EpdfToolbarCollapsedTemplate {
|
|
63
|
+
template = inject(TemplateRef);
|
|
64
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCollapsedTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
68
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfToolbarCollapsedTemplate, isStandalone: true, selector: "ng-template[epdfToolbarCollapsed]", ngImport: i0 });
|
|
69
|
+
}
|
|
70
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCollapsedTemplate, decorators: [{
|
|
71
|
+
type: Directive,
|
|
72
|
+
args: [{ selector: 'ng-template[epdfToolbarCollapsed]' }]
|
|
73
|
+
}] });
|
|
74
|
+
/**
|
|
75
|
+
* The button of a group that sheds items, and its menu: the template keeps its own open state.
|
|
76
|
+
* Without it: a chevron opening a menu of the shed items.
|
|
77
|
+
*/
|
|
78
|
+
class EpdfToolbarGroupTriggerTemplate {
|
|
79
|
+
template = inject(TemplateRef);
|
|
80
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarGroupTriggerTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
84
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfToolbarGroupTriggerTemplate, isStandalone: true, selector: "ng-template[epdfToolbarGroupTrigger]", ngImport: i0 });
|
|
85
|
+
}
|
|
86
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarGroupTriggerTemplate, decorators: [{
|
|
87
|
+
type: Directive,
|
|
88
|
+
args: [{ selector: 'ng-template[epdfToolbarGroupTrigger]' }]
|
|
89
|
+
}] });
|
|
90
|
+
/**
|
|
91
|
+
* The line between two groups. Without it: a 1px line across the toolbar. Tell the toolbar its
|
|
92
|
+
* thickness with `[separatorWidth]` when yours is thicker.
|
|
93
|
+
*/
|
|
94
|
+
class EpdfToolbarSeparatorTemplate {
|
|
95
|
+
template = inject(TemplateRef);
|
|
96
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarSeparatorTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
97
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfToolbarSeparatorTemplate, isStandalone: true, selector: "ng-template[epdfToolbarSeparator]", ngImport: i0 });
|
|
98
|
+
}
|
|
99
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarSeparatorTemplate, decorators: [{
|
|
100
|
+
type: Directive,
|
|
101
|
+
args: [{ selector: 'ng-template[epdfToolbarSeparator]' }]
|
|
102
|
+
}] });
|
|
103
|
+
/** The "More" button. Without it: a plain ⋯ button. */
|
|
104
|
+
class EpdfToolbarOverflowTriggerTemplate {
|
|
105
|
+
template = inject(TemplateRef);
|
|
106
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarOverflowTriggerTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
110
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfToolbarOverflowTriggerTemplate, isStandalone: true, selector: "ng-template[epdfToolbarOverflowTrigger]", ngImport: i0 });
|
|
111
|
+
}
|
|
112
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarOverflowTriggerTemplate, decorators: [{
|
|
113
|
+
type: Directive,
|
|
114
|
+
args: [{ selector: 'ng-template[epdfToolbarOverflowTrigger]' }]
|
|
115
|
+
}] });
|
|
116
|
+
/** The "More" menu, under its button (beside it, standing up). Without it: a plain popover. */
|
|
117
|
+
class EpdfToolbarOverflowMenuTemplate {
|
|
118
|
+
template = inject(TemplateRef);
|
|
119
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarOverflowMenuTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
123
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfToolbarOverflowMenuTemplate, isStandalone: true, selector: "ng-template[epdfToolbarOverflowMenu]", ngImport: i0 });
|
|
124
|
+
}
|
|
125
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarOverflowMenuTemplate, decorators: [{
|
|
126
|
+
type: Directive,
|
|
127
|
+
args: [{ selector: 'ng-template[epdfToolbarOverflowMenu]' }]
|
|
128
|
+
}] });
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The toolbar's plain parts, for every part the app leaves out: the "More" button, a popover
|
|
132
|
+
* menu, a folded group and a group's shed-items button. They're meant to be replaced, so their
|
|
133
|
+
* colors come from the `--epdf-toolbar-*` CSS variables only. The menu takes the menu keys
|
|
134
|
+
* (`attachMenuKeys`) while it's open.
|
|
135
|
+
*/
|
|
136
|
+
const SURFACE = paintDefault('toolbar-surface');
|
|
137
|
+
const BORDER = paintDefault('toolbar-border');
|
|
138
|
+
const ACTIVE = paintDefault('toolbar-active');
|
|
139
|
+
/** The colors, for the toolbar's own default command button and separator. */
|
|
140
|
+
const TOOLBAR_PAINT = { surface: SURFACE, border: BORDER, active: ACTIVE };
|
|
141
|
+
/** Where a menu opens: under a horizontal toolbar's button, beside a vertical one's. */
|
|
142
|
+
const placementOf = (orientation) => orientation === 'vertical'
|
|
143
|
+
? 'top: 0; inset-inline-start: 100%;'
|
|
144
|
+
: 'top: 100%; inset-inline-end: 0;';
|
|
145
|
+
/** The plain "More" button: ⋯, pressed while its menu is open. */
|
|
146
|
+
class EpdfToolbarMoreButton {
|
|
147
|
+
open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
148
|
+
toggle = output();
|
|
149
|
+
active = ACTIVE;
|
|
150
|
+
style = 'display: inline-flex; align-items: center; padding: 4px 8px; color: inherit; ' +
|
|
151
|
+
`font: inherit; border: 1px solid ${BORDER}; border-radius: 4px; cursor: pointer;`;
|
|
152
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarMoreButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
153
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.4", type: EpdfToolbarMoreButton, isStandalone: true, selector: "epdf-toolbar-more-button", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggle: "toggle" }, host: { styleAttribute: "display: contents;" }, ngImport: i0, template: `
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
aria-haspopup="menu"
|
|
157
|
+
[attr.aria-expanded]="open()"
|
|
158
|
+
title="More"
|
|
159
|
+
[style]="style"
|
|
160
|
+
[style.background]="open() ? active : 'transparent'"
|
|
161
|
+
(click)="toggle.emit()"
|
|
162
|
+
>
|
|
163
|
+
⋯
|
|
164
|
+
</button>
|
|
165
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
166
|
+
}
|
|
167
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarMoreButton, decorators: [{
|
|
168
|
+
type: Component,
|
|
169
|
+
args: [{
|
|
170
|
+
selector: 'epdf-toolbar-more-button',
|
|
171
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
172
|
+
host: { style: 'display: contents;' },
|
|
173
|
+
template: `
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
aria-haspopup="menu"
|
|
177
|
+
[attr.aria-expanded]="open()"
|
|
178
|
+
title="More"
|
|
179
|
+
[style]="style"
|
|
180
|
+
[style.background]="open() ? active : 'transparent'"
|
|
181
|
+
(click)="toggle.emit()"
|
|
182
|
+
>
|
|
183
|
+
⋯
|
|
184
|
+
</button>
|
|
185
|
+
`,
|
|
186
|
+
}]
|
|
187
|
+
}] });
|
|
188
|
+
/**
|
|
189
|
+
* The plain popover menu, under its button in a horizontal toolbar and beside it in a vertical
|
|
190
|
+
* one: a press outside closes it, a row runs its command, and while it's open it takes the menu
|
|
191
|
+
* keys: focus on its first row, the arrows, typeahead, Enter, and Escape back to its button.
|
|
192
|
+
*/
|
|
193
|
+
class EpdfToolbarMenu {
|
|
194
|
+
view = input.required(...(ngDevMode ? [{ debugName: "view" }] : []));
|
|
195
|
+
/** Which way the toolbar runs: the menu opens under its button, or beside a vertical one's. */
|
|
196
|
+
orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
|
|
197
|
+
/**
|
|
198
|
+
* Whether it's open, apart from the view: the toolbar hands a new view on every change, and
|
|
199
|
+
* attaching the keys again would put focus back on the first row.
|
|
200
|
+
*/
|
|
201
|
+
isOpen = computed(() => this.view().isOpen, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
202
|
+
menu = viewChild('menu', ...(ngDevMode ? [{ debugName: "menu" }] : []));
|
|
203
|
+
constructor() {
|
|
204
|
+
const host = inject(ElementRef).nativeElement;
|
|
205
|
+
afterRenderEffect((onCleanup) => {
|
|
206
|
+
const menu = this.menu()?.nativeElement;
|
|
207
|
+
if (!this.isOpen() || !menu)
|
|
208
|
+
return;
|
|
209
|
+
onCleanup(untracked(() => this.attach(host, menu)));
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/** Close on a press outside, and take the menu keys; returns the detach. */
|
|
213
|
+
attach(host, menu) {
|
|
214
|
+
// This element is `display: contents`; its parent holds the menu's button too, so a press
|
|
215
|
+
// on the button is the button's own toggle.
|
|
216
|
+
const container = host.parentElement ?? host;
|
|
217
|
+
const trigger = focusablesIn(container).find((candidate) => !menu.contains(candidate)) ?? null;
|
|
218
|
+
const close = () => untracked(this.view).close();
|
|
219
|
+
const stopOutside = observeOutsidePress(container, close);
|
|
220
|
+
const detachKeys = attachMenuKeys(menu, { trigger, close });
|
|
221
|
+
return () => {
|
|
222
|
+
stopOutside();
|
|
223
|
+
detachKeys();
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
menuStyle = computed(() => `position: absolute; ${placementOf(this.orientation())} z-index: 41; min-width: 200px; ` +
|
|
227
|
+
`padding: 4px; background: ${SURFACE}; border: 1px solid ${BORDER}; border-radius: 6px; ` +
|
|
228
|
+
'box-shadow: 0 4px 16px rgba(0,0,0,0.15);', ...(ngDevMode ? [{ debugName: "menuStyle" }] : []));
|
|
229
|
+
dividerStyle = `height: 1px; background: ${BORDER}; margin: 4px 0;`;
|
|
230
|
+
rowStyle = 'display: flex; width: 100%; align-items: center; justify-content: space-between; ' +
|
|
231
|
+
'gap: 16px; padding: 6px 8px; border: none; color: inherit; font: inherit; ' +
|
|
232
|
+
'background: transparent; white-space: nowrap;';
|
|
233
|
+
/** Run the row's command; a row that opens a submenu keeps the menu open. */
|
|
234
|
+
choose(row) {
|
|
235
|
+
const view = this.view();
|
|
236
|
+
view.execute(row.command);
|
|
237
|
+
if (row.type !== 'submenu')
|
|
238
|
+
view.close();
|
|
239
|
+
}
|
|
240
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
241
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfToolbarMenu, isStandalone: true, selector: "epdf-toolbar-menu", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: contents;" }, viewQueries: [{ propertyName: "menu", first: true, predicate: ["menu"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
242
|
+
@if (isOpen()) {
|
|
243
|
+
<div #menu role="menu" [style]="menuStyle()">
|
|
244
|
+
@for (section of view().sections; track $index; let first = $first) {
|
|
245
|
+
@if (!first) {
|
|
246
|
+
<div [style]="dividerStyle"></div>
|
|
247
|
+
}
|
|
248
|
+
@for (row of section.rows; track row.command) {
|
|
249
|
+
@if (view().resolve(row.command); as command) {
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
[attr.role]="section.role === 'radio' ? 'menuitemradio' : 'menuitem'"
|
|
253
|
+
[attr.aria-checked]="section.role === 'radio' ? command.active : null"
|
|
254
|
+
[disabled]="!command.enabled"
|
|
255
|
+
tabindex="-1"
|
|
256
|
+
[style]="rowStyle"
|
|
257
|
+
[style.cursor]="command.enabled ? 'pointer' : 'default'"
|
|
258
|
+
[style.opacity]="command.enabled ? 1 : 0.4"
|
|
259
|
+
(click)="choose(row)"
|
|
260
|
+
>
|
|
261
|
+
<span
|
|
262
|
+
>{{ command.active && section.role === 'radio' ? '• ' : ''
|
|
263
|
+
}}{{ command.label }}</span
|
|
264
|
+
>
|
|
265
|
+
@if (row.type === 'submenu') {
|
|
266
|
+
<span>▸</span>
|
|
267
|
+
}
|
|
268
|
+
</button>
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
</div>
|
|
273
|
+
}
|
|
274
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
275
|
+
}
|
|
276
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarMenu, decorators: [{
|
|
277
|
+
type: Component,
|
|
278
|
+
args: [{
|
|
279
|
+
selector: 'epdf-toolbar-menu',
|
|
280
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
281
|
+
host: { style: 'display: contents;' },
|
|
282
|
+
template: `
|
|
283
|
+
@if (isOpen()) {
|
|
284
|
+
<div #menu role="menu" [style]="menuStyle()">
|
|
285
|
+
@for (section of view().sections; track $index; let first = $first) {
|
|
286
|
+
@if (!first) {
|
|
287
|
+
<div [style]="dividerStyle"></div>
|
|
288
|
+
}
|
|
289
|
+
@for (row of section.rows; track row.command) {
|
|
290
|
+
@if (view().resolve(row.command); as command) {
|
|
291
|
+
<button
|
|
292
|
+
type="button"
|
|
293
|
+
[attr.role]="section.role === 'radio' ? 'menuitemradio' : 'menuitem'"
|
|
294
|
+
[attr.aria-checked]="section.role === 'radio' ? command.active : null"
|
|
295
|
+
[disabled]="!command.enabled"
|
|
296
|
+
tabindex="-1"
|
|
297
|
+
[style]="rowStyle"
|
|
298
|
+
[style.cursor]="command.enabled ? 'pointer' : 'default'"
|
|
299
|
+
[style.opacity]="command.enabled ? 1 : 0.4"
|
|
300
|
+
(click)="choose(row)"
|
|
301
|
+
>
|
|
302
|
+
<span
|
|
303
|
+
>{{ command.active && section.role === 'radio' ? '• ' : ''
|
|
304
|
+
}}{{ command.label }}</span
|
|
305
|
+
>
|
|
306
|
+
@if (row.type === 'submenu') {
|
|
307
|
+
<span>▸</span>
|
|
308
|
+
}
|
|
309
|
+
</button>
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
</div>
|
|
314
|
+
}
|
|
315
|
+
`,
|
|
316
|
+
}]
|
|
317
|
+
}], ctorParameters: () => [] });
|
|
318
|
+
/** The plain folded group: a `<select>` for `collapse: 'select'`, a ⋯ menu button otherwise. */
|
|
319
|
+
class EpdfToolbarCollapsed {
|
|
320
|
+
view = input.required(...(ngDevMode ? [{ debugName: "view" }] : []));
|
|
321
|
+
/** Which way the toolbar runs: where the menu opens. */
|
|
322
|
+
orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
|
|
323
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
324
|
+
activeId = computed(() => this.view().commands.find((command) => command.active)?.id ?? null, ...(ngDevMode ? [{ debugName: "activeId" }] : []));
|
|
325
|
+
menu = computed(() => groupMenuView(this.view(), this.isOpen(), () => this.isOpen.set(false)), ...(ngDevMode ? [{ debugName: "menu" }] : []));
|
|
326
|
+
selectStyle = `padding: 4px 6px; font: inherit; border-radius: 4px; border: 1px solid ${BORDER}; ` +
|
|
327
|
+
`background: ${SURFACE};`;
|
|
328
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCollapsed, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
329
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfToolbarCollapsed, isStandalone: true, selector: "epdf-toolbar-collapsed", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: contents;" }, ngImport: i0, template: `
|
|
330
|
+
@if (view().collapse === 'select') {
|
|
331
|
+
<select #select [style]="selectStyle" (change)="view().execute(select.value)">
|
|
332
|
+
@if (!activeId()) {
|
|
333
|
+
<option value="" selected disabled hidden></option>
|
|
334
|
+
}
|
|
335
|
+
@for (command of view().commands; track command.id) {
|
|
336
|
+
<option
|
|
337
|
+
[value]="command.id"
|
|
338
|
+
[disabled]="!command.enabled"
|
|
339
|
+
[selected]="command.id === activeId()"
|
|
340
|
+
>
|
|
341
|
+
{{ command.label }}
|
|
342
|
+
</option>
|
|
343
|
+
}
|
|
344
|
+
</select>
|
|
345
|
+
} @else {
|
|
346
|
+
<span style="position: relative; display: inline-flex">
|
|
347
|
+
<epdf-toolbar-more-button [open]="isOpen()" (toggle)="isOpen.set(!isOpen())" />
|
|
348
|
+
<epdf-toolbar-menu [view]="menu()" [orientation]="orientation()" />
|
|
349
|
+
</span>
|
|
350
|
+
}
|
|
351
|
+
`, isInline: true, dependencies: [{ kind: "component", type: EpdfToolbarMoreButton, selector: "epdf-toolbar-more-button", inputs: ["open"], outputs: ["toggle"] }, { kind: "component", type: EpdfToolbarMenu, selector: "epdf-toolbar-menu", inputs: ["view", "orientation"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
352
|
+
}
|
|
353
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarCollapsed, decorators: [{
|
|
354
|
+
type: Component,
|
|
355
|
+
args: [{
|
|
356
|
+
selector: 'epdf-toolbar-collapsed',
|
|
357
|
+
imports: [EpdfToolbarMoreButton, EpdfToolbarMenu],
|
|
358
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
359
|
+
host: { style: 'display: contents;' },
|
|
360
|
+
template: `
|
|
361
|
+
@if (view().collapse === 'select') {
|
|
362
|
+
<select #select [style]="selectStyle" (change)="view().execute(select.value)">
|
|
363
|
+
@if (!activeId()) {
|
|
364
|
+
<option value="" selected disabled hidden></option>
|
|
365
|
+
}
|
|
366
|
+
@for (command of view().commands; track command.id) {
|
|
367
|
+
<option
|
|
368
|
+
[value]="command.id"
|
|
369
|
+
[disabled]="!command.enabled"
|
|
370
|
+
[selected]="command.id === activeId()"
|
|
371
|
+
>
|
|
372
|
+
{{ command.label }}
|
|
373
|
+
</option>
|
|
374
|
+
}
|
|
375
|
+
</select>
|
|
376
|
+
} @else {
|
|
377
|
+
<span style="position: relative; display: inline-flex">
|
|
378
|
+
<epdf-toolbar-more-button [open]="isOpen()" (toggle)="isOpen.set(!isOpen())" />
|
|
379
|
+
<epdf-toolbar-menu [view]="menu()" [orientation]="orientation()" />
|
|
380
|
+
</span>
|
|
381
|
+
}
|
|
382
|
+
`,
|
|
383
|
+
}]
|
|
384
|
+
}] });
|
|
385
|
+
/** The plain button of a group that shed items: a chevron opening a menu of them. */
|
|
386
|
+
class EpdfToolbarGroupTrigger {
|
|
387
|
+
view = input.required(...(ngDevMode ? [{ debugName: "view" }] : []));
|
|
388
|
+
/** Which way the toolbar runs: where the menu opens. */
|
|
389
|
+
orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
|
|
390
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
391
|
+
/** A hint that the active item is in here. */
|
|
392
|
+
someActive = computed(() => this.view().commands.some((command) => command.active), ...(ngDevMode ? [{ debugName: "someActive" }] : []));
|
|
393
|
+
menu = computed(() => groupMenuView(this.view(), this.isOpen(), () => this.isOpen.set(false)), ...(ngDevMode ? [{ debugName: "menu" }] : []));
|
|
394
|
+
active = ACTIVE;
|
|
395
|
+
buttonStyle = 'display: inline-flex; align-items: center; padding: 4px 6px; color: inherit; ' +
|
|
396
|
+
`font: inherit; border: 1px solid ${BORDER}; border-radius: 4px; cursor: pointer;`;
|
|
397
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarGroupTrigger, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
398
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.4", type: EpdfToolbarGroupTrigger, isStandalone: true, selector: "epdf-toolbar-group-trigger", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: contents;" }, ngImport: i0, template: `
|
|
399
|
+
<span style="position: relative; display: inline-flex">
|
|
400
|
+
<button
|
|
401
|
+
type="button"
|
|
402
|
+
aria-haspopup="menu"
|
|
403
|
+
[attr.aria-expanded]="isOpen()"
|
|
404
|
+
title="More"
|
|
405
|
+
[style]="buttonStyle"
|
|
406
|
+
[style.background]="isOpen() || someActive() ? active : 'transparent'"
|
|
407
|
+
(click)="isOpen.set(!isOpen())"
|
|
408
|
+
>
|
|
409
|
+
▾
|
|
410
|
+
</button>
|
|
411
|
+
<epdf-toolbar-menu [view]="menu()" [orientation]="orientation()" />
|
|
412
|
+
</span>
|
|
413
|
+
`, isInline: true, dependencies: [{ kind: "component", type: EpdfToolbarMenu, selector: "epdf-toolbar-menu", inputs: ["view", "orientation"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
414
|
+
}
|
|
415
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarGroupTrigger, decorators: [{
|
|
416
|
+
type: Component,
|
|
417
|
+
args: [{
|
|
418
|
+
selector: 'epdf-toolbar-group-trigger',
|
|
419
|
+
imports: [EpdfToolbarMenu],
|
|
420
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
421
|
+
host: { style: 'display: contents;' },
|
|
422
|
+
template: `
|
|
423
|
+
<span style="position: relative; display: inline-flex">
|
|
424
|
+
<button
|
|
425
|
+
type="button"
|
|
426
|
+
aria-haspopup="menu"
|
|
427
|
+
[attr.aria-expanded]="isOpen()"
|
|
428
|
+
title="More"
|
|
429
|
+
[style]="buttonStyle"
|
|
430
|
+
[style.background]="isOpen() || someActive() ? active : 'transparent'"
|
|
431
|
+
(click)="isOpen.set(!isOpen())"
|
|
432
|
+
>
|
|
433
|
+
▾
|
|
434
|
+
</button>
|
|
435
|
+
<epdf-toolbar-menu [view]="menu()" [orientation]="orientation()" />
|
|
436
|
+
</span>
|
|
437
|
+
`,
|
|
438
|
+
}]
|
|
439
|
+
}] });
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* `<epdf-toolbar [bar]>`: a toolbar that measures what fits and makes room, and lets you draw
|
|
443
|
+
* every part.
|
|
444
|
+
*
|
|
445
|
+
* 1. a hidden measurement layer draws every item in every variant (plus folded groups and the
|
|
446
|
+
* "More" button), each watched by `@embedpdf/web`'s `observeLength` along the toolbar
|
|
447
|
+
* (widths, or heights when it is vertical), so a new language, a font loading, browser zoom
|
|
448
|
+
* and your CSS all re-measure on their own; the lengths become fit metrics
|
|
449
|
+
* (`createToolbarLengths`);
|
|
450
|
+
* 2. core-ui's `solve()` gives each item a variant, a folded group or the "More" menu, and
|
|
451
|
+
* `layoutToolbar` turns that fit into the parts to draw;
|
|
452
|
+
* 3. the live row draws them; the "More" menu is derived (`projectOverflow`) from what
|
|
453
|
+
* didn't fit.
|
|
454
|
+
*
|
|
455
|
+
* The toolbar is one tab stop with arrow keys along it (`attachToolbarFocus`), and its default
|
|
456
|
+
* menus take the menu keys (`attachMenuKeys`). Every part is an `<ng-template>` you may give
|
|
457
|
+
* (`templates.ts`), with a plain default (`defaults.ts`): the app owns the pixels, the toolbar
|
|
458
|
+
* owns the fitting and the keyboard. The items are commands, read live, so a button follows its
|
|
459
|
+
* command's state with no code in the toolbar.
|
|
460
|
+
*/
|
|
461
|
+
/** What the toolbar draws while there is no commands plugin: nothing. */
|
|
462
|
+
const EMPTY_LAYOUT = {
|
|
463
|
+
sections: [
|
|
464
|
+
{ name: 'start', groups: [] },
|
|
465
|
+
{ name: 'center', groups: [] },
|
|
466
|
+
{ name: 'end', groups: [] },
|
|
467
|
+
],
|
|
468
|
+
measured: [],
|
|
469
|
+
hasOverflow: false,
|
|
470
|
+
overflow: [],
|
|
471
|
+
};
|
|
472
|
+
const noop = () => { };
|
|
473
|
+
const TOOLBAR_LENGTHS = new InjectionToken('TOOLBAR_LENGTHS');
|
|
474
|
+
/** Whether `element` draws anything: an element, or some text, among its children. */
|
|
475
|
+
const drawsSomething = (element) => [...element.childNodes].some((node) => node.nodeType === Node.ELEMENT_NODE ||
|
|
476
|
+
(node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim())));
|
|
477
|
+
/**
|
|
478
|
+
* Reports its element's border-box length along the toolbar (its width, or its height in a
|
|
479
|
+
* vertical one) to the toolbar, under its key, now and on resize. With `ifDrawn`, only while the
|
|
480
|
+
* element draws something: an item of your own whose template draws nothing in the measurement
|
|
481
|
+
* layer reports its length itself, with `measure`, from where it renders.
|
|
482
|
+
*/
|
|
483
|
+
class EpdfToolbarLength {
|
|
484
|
+
key = input.required(...(ngDevMode ? [{ debugName: "key", alias: 'epdfToolbarLength' }] : [{ alias: 'epdfToolbarLength' }]));
|
|
485
|
+
ifDrawn = input(false, ...(ngDevMode ? [{ debugName: "ifDrawn" }] : []));
|
|
486
|
+
constructor() {
|
|
487
|
+
const element = inject(ElementRef).nativeElement;
|
|
488
|
+
const sink = inject(TOOLBAR_LENGTHS);
|
|
489
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
490
|
+
return;
|
|
491
|
+
effect((onCleanup) => {
|
|
492
|
+
const key = this.key();
|
|
493
|
+
const orientation = sink.orientation();
|
|
494
|
+
onCleanup(untracked(() => observeLength(element, orientation, (length) => {
|
|
495
|
+
if (this.ifDrawn() && !drawsSomething(element))
|
|
496
|
+
return;
|
|
497
|
+
sink.reportLength(key, length);
|
|
498
|
+
})));
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarLength, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
502
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.2.4", type: EpdfToolbarLength, isStandalone: true, selector: "[epdfToolbarLength]", inputs: { key: { classPropertyName: "key", publicName: "epdfToolbarLength", isSignal: true, isRequired: true, transformFunction: null }, ifDrawn: { classPropertyName: "ifDrawn", publicName: "ifDrawn", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
503
|
+
}
|
|
504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbarLength, decorators: [{
|
|
505
|
+
type: Directive,
|
|
506
|
+
args: [{ selector: '[epdfToolbarLength]' }]
|
|
507
|
+
}], ctorParameters: () => [] });
|
|
508
|
+
// ── the toolbar ──────────────────────────────────────────────────────────────
|
|
509
|
+
/**
|
|
510
|
+
* A toolbar that fits itself to its width, or its height with `orientation="vertical"`: items
|
|
511
|
+
* get smaller, groups fold, and what's left over goes into a "More" menu, the least important
|
|
512
|
+
* first. `[gap]` (8) is the space between items, `[separatorWidth]` (1) your
|
|
513
|
+
* separator's thickness; the element is the toolbar (`role="toolbar"`), so a `class`, `style`
|
|
514
|
+
* or `aria-label` on it is the toolbar's. It is one tab stop, with arrow keys between its
|
|
515
|
+
* items. Draw the parts with the templates in `templates.ts`.
|
|
516
|
+
*
|
|
517
|
+
* <epdf-toolbar [bar]="bar">
|
|
518
|
+
* <ng-template epdfToolbarCommand let-command let-variant="variant" let-run="run">
|
|
519
|
+
* <button (click)="run()" [disabled]="!command.enabled">{{ command.label }}</button>
|
|
520
|
+
* </ng-template>
|
|
521
|
+
* </epdf-toolbar>
|
|
522
|
+
*/
|
|
523
|
+
class EpdfToolbar {
|
|
524
|
+
/** What the toolbar holds: `{ id, sections: { start, center, end } }` of groups and items. */
|
|
525
|
+
bar = input.required(...(ngDevMode ? [{ debugName: "bar" }] : []));
|
|
526
|
+
/**
|
|
527
|
+
* Which way the toolbar runs. A vertical toolbar fits the height it's given, so give it one;
|
|
528
|
+
* it stacks its sections top, middle and bottom, and draws every command at `'icon'`, with
|
|
529
|
+
* the label for a tooltip.
|
|
530
|
+
*/
|
|
531
|
+
orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
|
|
532
|
+
/** Pixels between neighbouring items; the solver counts the same number. */
|
|
533
|
+
gap = input(8, ...(ngDevMode ? [{ debugName: "gap" }] : []));
|
|
534
|
+
/**
|
|
535
|
+
* The thickness of the separator you draw: its width across a horizontal toolbar, its height
|
|
536
|
+
* in a vertical one.
|
|
537
|
+
*/
|
|
538
|
+
separatorWidth = input(1, ...(ngDevMode ? [{ debugName: "separatorWidth" }] : []));
|
|
539
|
+
// ── the app's templates ─────────────────────────────────────────────────────
|
|
540
|
+
commandTemplate = contentChild(EpdfToolbarCommandTemplate, ...(ngDevMode ? [{ debugName: "commandTemplate" }] : []));
|
|
541
|
+
customTemplates = contentChildren(EpdfToolbarCustomTemplate, ...(ngDevMode ? [{ debugName: "customTemplates" }] : []));
|
|
542
|
+
collapsedTemplate = contentChild(EpdfToolbarCollapsedTemplate, ...(ngDevMode ? [{ debugName: "collapsedTemplate" }] : []));
|
|
543
|
+
groupTriggerTemplate = contentChild(EpdfToolbarGroupTriggerTemplate, ...(ngDevMode ? [{ debugName: "groupTriggerTemplate" }] : []));
|
|
544
|
+
separatorTemplate = contentChild(EpdfToolbarSeparatorTemplate, ...(ngDevMode ? [{ debugName: "separatorTemplate" }] : []));
|
|
545
|
+
overflowTriggerTemplate = contentChild(EpdfToolbarOverflowTriggerTemplate, ...(ngDevMode ? [{ debugName: "overflowTriggerTemplate" }] : []));
|
|
546
|
+
overflowMenuTemplate = contentChild(EpdfToolbarOverflowMenuTemplate, ...(ngDevMode ? [{ debugName: "overflowMenuTemplate" }] : []));
|
|
547
|
+
// ── the commands ────────────────────────────────────────────────────────────
|
|
548
|
+
host = injectKernelHost('<epdf-toolbar>');
|
|
549
|
+
// Names withCommands() in the error when the viewer has no commands plugin, and runs them.
|
|
550
|
+
commands = inject(EpdfCommands);
|
|
551
|
+
/** The host lens, for this part of the template's document: resolves and finds menus. */
|
|
552
|
+
registry = new CapabilityBinding(this.host, () => CommandsToken, injectDocumentScope()).capability;
|
|
553
|
+
lengths = createToolbarLengths();
|
|
554
|
+
/** Goes up whenever a measured length changed by more than half a pixel. */
|
|
555
|
+
measureVersion = signal(0, ...(ngDevMode ? [{ debugName: "measureVersion" }] : []));
|
|
556
|
+
/** The room along the toolbar: its width, or its height when vertical. */
|
|
557
|
+
containerLength = signal(0, ...(ngDevMode ? [{ debugName: "containerLength" }] : []));
|
|
558
|
+
overflowOpen = signal(false, ...(ngDevMode ? [{ debugName: "overflowOpen" }] : []));
|
|
559
|
+
/** The arrow keys along the toolbar, while it's in the page. */
|
|
560
|
+
roving = null;
|
|
561
|
+
vertical = computed(() => this.orientation() === 'vertical', ...(ngDevMode ? [{ debugName: "vertical" }] : []));
|
|
562
|
+
normalized = computed(() => normalizeBar(this.bar()), ...(ngDevMode ? [{ debugName: "normalized" }] : []));
|
|
563
|
+
/**
|
|
564
|
+
* What to draw: the bar with what's visible now, fitted and turned into parts. A command's
|
|
565
|
+
* state is derived when it's read, so this reads again after every change of the viewer; the
|
|
566
|
+
* toolbar is small, and a read of every item per change is the simple, correct baseline.
|
|
567
|
+
*/
|
|
568
|
+
layout = this.host.read(() => {
|
|
569
|
+
this.measureVersion();
|
|
570
|
+
const registry = this.registry();
|
|
571
|
+
if (!registry)
|
|
572
|
+
return EMPTY_LAYOUT;
|
|
573
|
+
return layoutToolbar({
|
|
574
|
+
bar: this.normalized(),
|
|
575
|
+
resolve: (id) => registry.resolveCommand(id),
|
|
576
|
+
unregistered: unregisteredCommand,
|
|
577
|
+
execute: (id) => this.execute(id),
|
|
578
|
+
menuTarget: (id) => registry.getMenuTarget(id),
|
|
579
|
+
metrics: this.lengths.metrics(this.orientation(), this.gap(), this.separatorWidth()),
|
|
580
|
+
measureKey: toolbarMeasureKey,
|
|
581
|
+
containerLength: this.containerLength(),
|
|
582
|
+
orientation: this.orientation(),
|
|
583
|
+
});
|
|
584
|
+
}, () => EMPTY_LAYOUT);
|
|
585
|
+
hasOverflow = computed(() => this.layout().hasOverflow, ...(ngDevMode ? [{ debugName: "hasOverflow" }] : []));
|
|
586
|
+
row = computed(() => {
|
|
587
|
+
const [start, center, end] = this.layout().sections;
|
|
588
|
+
return {
|
|
589
|
+
start: this.liveEntries(start),
|
|
590
|
+
center: this.liveEntries(center),
|
|
591
|
+
end: this.liveEntries(end),
|
|
592
|
+
};
|
|
593
|
+
}, ...(ngDevMode ? [{ debugName: "row" }] : []));
|
|
594
|
+
/** Every item in every variant, each folded group and each shed group's button. */
|
|
595
|
+
measured = computed(() => this.layout().measured.flatMap((part) => {
|
|
596
|
+
const entry = this.entryOf(part, 'measure');
|
|
597
|
+
return entry ? [{ key: part.measureKey, entry }] : [];
|
|
598
|
+
}), ...(ngDevMode ? [{ debugName: "measured" }] : []));
|
|
599
|
+
overflowTrigger = computed(() => ({
|
|
600
|
+
$implicit: this.overflowOpen(),
|
|
601
|
+
toggle: () => this.overflowOpen.update((open) => !open),
|
|
602
|
+
}), ...(ngDevMode ? [{ debugName: "overflowTrigger" }] : []));
|
|
603
|
+
closedTrigger = {
|
|
604
|
+
$implicit: false,
|
|
605
|
+
toggle: noop,
|
|
606
|
+
};
|
|
607
|
+
overflowMenu = computed(() => {
|
|
608
|
+
return {
|
|
609
|
+
$implicit: {
|
|
610
|
+
sections: this.layout().overflow,
|
|
611
|
+
isOpen: this.overflowOpen(),
|
|
612
|
+
close: () => this.overflowOpen.set(false),
|
|
613
|
+
resolve: (id) => this.resolve(id),
|
|
614
|
+
execute: (id) => this.execute(id),
|
|
615
|
+
},
|
|
616
|
+
};
|
|
617
|
+
}, ...(ngDevMode ? [{ debugName: "overflowMenu" }] : []));
|
|
618
|
+
overflowTriggerKey = toolbarMeasureKey.overflowTrigger;
|
|
619
|
+
paint = TOOLBAR_PAINT;
|
|
620
|
+
sectionStyle = computed(() => `display: flex; flex-direction: ${this.vertical() ? 'column' : 'row'}; ` +
|
|
621
|
+
'align-items: center; flex: 0 0 auto;', ...(ngDevMode ? [{ debugName: "sectionStyle" }] : []));
|
|
622
|
+
centerStyle = computed(() => this.sectionStyle() +
|
|
623
|
+
(this.vertical()
|
|
624
|
+
? ' margin-top: auto; margin-bottom: auto;'
|
|
625
|
+
: ' margin-left: auto; margin-right: auto;'), ...(ngDevMode ? [{ debugName: "centerStyle" }] : []));
|
|
626
|
+
/** A 1px line across the toolbar: upright in a horizontal one, flat in a vertical one. */
|
|
627
|
+
separatorStyle = computed(() => `${this.vertical() ? 'height' : 'width'}: 1px; align-self: stretch; ` +
|
|
628
|
+
'background: currentColor; opacity: 0.2;', ...(ngDevMode ? [{ debugName: "separatorStyle" }] : []));
|
|
629
|
+
/**
|
|
630
|
+
* A row for a horizontal toolbar, a column for a vertical one, and no height either way, so it
|
|
631
|
+
* never adds to the page. A column keeps each item at its own width, so no text wraps and
|
|
632
|
+
* changes the heights it reports.
|
|
633
|
+
*/
|
|
634
|
+
measureLayerStyle = computed(() => 'position: absolute; left: 0; top: 0; height: 0; overflow: hidden; visibility: hidden; ' +
|
|
635
|
+
'pointer-events: none; display: flex; white-space: nowrap;' +
|
|
636
|
+
(this.vertical() ? ' flex-direction: column; align-items: flex-start;' : ''), ...(ngDevMode ? [{ debugName: "measureLayerStyle" }] : []));
|
|
637
|
+
commandStyle = 'display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; ' +
|
|
638
|
+
`white-space: nowrap; color: inherit; font: inherit; border: 1px solid ${TOOLBAR_PAINT.border}; ` +
|
|
639
|
+
'border-radius: 4px;';
|
|
640
|
+
constructor() {
|
|
641
|
+
// The "More" menu closes when everything fits again.
|
|
642
|
+
effect(() => {
|
|
643
|
+
if (!this.hasOverflow())
|
|
644
|
+
untracked(() => this.overflowOpen.set(false));
|
|
645
|
+
});
|
|
646
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
647
|
+
return;
|
|
648
|
+
const element = inject(ElementRef).nativeElement;
|
|
649
|
+
// The room the toolbar fills, along the way it runs.
|
|
650
|
+
effect((onCleanup) => {
|
|
651
|
+
const orientation = this.orientation();
|
|
652
|
+
onCleanup(untracked(() => observeContentLength(element, orientation, (length) => this.containerLength.set(length))));
|
|
653
|
+
});
|
|
654
|
+
// The keyboard: one tab stop, and arrow keys along the toolbar.
|
|
655
|
+
afterRenderEffect((onCleanup) => {
|
|
656
|
+
const orientation = this.orientation();
|
|
657
|
+
const focus = untracked(() => attachToolbarFocus(element, orientation));
|
|
658
|
+
this.roving = focus;
|
|
659
|
+
onCleanup(() => {
|
|
660
|
+
focus.detach();
|
|
661
|
+
this.roving = null;
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
// Any draw can add, remove or disable an item, so the tab stop is checked after each one.
|
|
665
|
+
afterRenderEffect(() => {
|
|
666
|
+
this.row();
|
|
667
|
+
this.hasOverflow();
|
|
668
|
+
this.overflowOpen();
|
|
669
|
+
untracked(() => this.roving?.refresh());
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
/** A measured element's length along the toolbar; a change solves again. */
|
|
673
|
+
reportLength(key, length) {
|
|
674
|
+
if (this.lengths.report(untracked(this.orientation), key, length)) {
|
|
675
|
+
this.measureVersion.update((version) => version + 1);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// ── building the entries ────────────────────────────────────────────────────
|
|
679
|
+
resolve(id) {
|
|
680
|
+
return untracked(this.registry)?.resolveCommand(id) ?? null;
|
|
681
|
+
}
|
|
682
|
+
execute(id) {
|
|
683
|
+
// A command whose `run` throws reports it through the commands' `executionFailed$`.
|
|
684
|
+
this.commands.execute(id).catch(noop);
|
|
685
|
+
}
|
|
686
|
+
customTemplateFor(slot) {
|
|
687
|
+
return this.customTemplates().find((template) => template.slot() === slot);
|
|
688
|
+
}
|
|
689
|
+
/** A section's groups, with a separator before every group but the first. */
|
|
690
|
+
liveEntries(section) {
|
|
691
|
+
const entries = [];
|
|
692
|
+
section?.groups.forEach((group, index) => {
|
|
693
|
+
if (index > 0)
|
|
694
|
+
entries.push({ kind: 'separator', key: `separator:${group.id}` });
|
|
695
|
+
for (const part of group.parts) {
|
|
696
|
+
const entry = this.entryOf(part, 'live');
|
|
697
|
+
if (entry)
|
|
698
|
+
entries.push(entry);
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
return entries;
|
|
702
|
+
}
|
|
703
|
+
/** A part with its template's context; null for an item of your own with no template, when measuring. */
|
|
704
|
+
entryOf(part, layer) {
|
|
705
|
+
const key = part.measureKey;
|
|
706
|
+
switch (part.kind) {
|
|
707
|
+
case 'command':
|
|
708
|
+
return {
|
|
709
|
+
kind: 'command',
|
|
710
|
+
key,
|
|
711
|
+
context: { $implicit: part.command, variant: part.variant, run: part.run },
|
|
712
|
+
};
|
|
713
|
+
case 'custom': {
|
|
714
|
+
const custom = this.customTemplateFor(part.name);
|
|
715
|
+
if (custom) {
|
|
716
|
+
return {
|
|
717
|
+
kind: 'custom',
|
|
718
|
+
key,
|
|
719
|
+
template: custom.template,
|
|
720
|
+
context: {
|
|
721
|
+
$implicit: part.variant,
|
|
722
|
+
layer,
|
|
723
|
+
measure: (length) => this.reportLength(key, length),
|
|
724
|
+
},
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
// An item of your own with no template is measured where it renders: only the first
|
|
728
|
+
// `<slot>` of a name gets the projected content, so it can't be drawn here too.
|
|
729
|
+
if (layer === 'measure')
|
|
730
|
+
return null;
|
|
731
|
+
return {
|
|
732
|
+
kind: 'socket',
|
|
733
|
+
key,
|
|
734
|
+
slot: part.name,
|
|
735
|
+
context: part.terminal && {
|
|
736
|
+
$implicit: part.terminal,
|
|
737
|
+
variant: 'icon',
|
|
738
|
+
run: part.runTerminal,
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
case 'collapsed':
|
|
743
|
+
return { kind: 'collapsed', key, context: { $implicit: part.view } };
|
|
744
|
+
case 'disclosure':
|
|
745
|
+
return { kind: 'trigger', key, context: { $implicit: part.view } };
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
749
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfToolbar, isStandalone: true, selector: "epdf-toolbar", inputs: { bar: { classPropertyName: "bar", publicName: "bar", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, separatorWidth: { classPropertyName: "separatorWidth", publicName: "separatorWidth", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, properties: { "attr.aria-orientation": "orientation()", "style.flex-direction": "orientation() === 'vertical' ? 'column' : 'row'" }, styleAttribute: "position: relative; display: flex; align-items: center;" }, providers: [{ provide: TOOLBAR_LENGTHS, useExisting: forwardRef(() => EpdfToolbar) }], queries: [{ propertyName: "commandTemplate", first: true, predicate: EpdfToolbarCommandTemplate, descendants: true, isSignal: true }, { propertyName: "customTemplates", predicate: EpdfToolbarCustomTemplate, isSignal: true }, { propertyName: "collapsedTemplate", first: true, predicate: EpdfToolbarCollapsedTemplate, descendants: true, isSignal: true }, { propertyName: "groupTriggerTemplate", first: true, predicate: EpdfToolbarGroupTriggerTemplate, descendants: true, isSignal: true }, { propertyName: "separatorTemplate", first: true, predicate: EpdfToolbarSeparatorTemplate, descendants: true, isSignal: true }, { propertyName: "overflowTriggerTemplate", first: true, predicate: EpdfToolbarOverflowTriggerTemplate, descendants: true, isSignal: true }, { propertyName: "overflowMenuTemplate", first: true, predicate: EpdfToolbarOverflowMenuTemplate, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
750
|
+
<!--
|
|
751
|
+
The sections run along the toolbar: the center one has auto margins, so it stays centered
|
|
752
|
+
in the space the others leave and gives way before anything overlaps. The sections never
|
|
753
|
+
grow or shrink: what fits is the solver's decision, so the row's least length is exactly
|
|
754
|
+
what it counted.
|
|
755
|
+
-->
|
|
756
|
+
<div [style]="sectionStyle()" [style.gap.px]="gap()">
|
|
757
|
+
@for (entry of row().start; track entry.key) {
|
|
758
|
+
<ng-container *ngTemplateOutlet="liveEntry; context: { $implicit: entry }" />
|
|
759
|
+
}
|
|
760
|
+
</div>
|
|
761
|
+
<div [style]="centerStyle()" [style.gap.px]="gap()">
|
|
762
|
+
@for (entry of row().center; track entry.key) {
|
|
763
|
+
<ng-container *ngTemplateOutlet="liveEntry; context: { $implicit: entry }" />
|
|
764
|
+
}
|
|
765
|
+
</div>
|
|
766
|
+
<div [style]="sectionStyle()" [style.gap.px]="gap()">
|
|
767
|
+
@for (entry of row().end; track entry.key) {
|
|
768
|
+
<ng-container *ngTemplateOutlet="liveEntry; context: { $implicit: entry }" />
|
|
769
|
+
}
|
|
770
|
+
@if (hasOverflow()) {
|
|
771
|
+
<span data-epdf-toolbar-item style="position: relative; display: inline-flex">
|
|
772
|
+
<ng-container
|
|
773
|
+
*ngTemplateOutlet="
|
|
774
|
+
overflowTriggerTemplate()?.template ?? defaultOverflowTrigger;
|
|
775
|
+
context: overflowTrigger()
|
|
776
|
+
"
|
|
777
|
+
/>
|
|
778
|
+
<ng-container
|
|
779
|
+
*ngTemplateOutlet="
|
|
780
|
+
overflowMenuTemplate()?.template ?? defaultOverflowMenu;
|
|
781
|
+
context: overflowMenu()
|
|
782
|
+
"
|
|
783
|
+
/>
|
|
784
|
+
</span>
|
|
785
|
+
}
|
|
786
|
+
</div>
|
|
787
|
+
|
|
788
|
+
<!--
|
|
789
|
+
The measurement layer: every item in every variant, every folded group and the "More"
|
|
790
|
+
button, hidden, inert and observed. Text reflow (a language, fonts, zoom) re-measures by
|
|
791
|
+
itself. An item of your own whose template draws nothing here is measured where it renders.
|
|
792
|
+
-->
|
|
793
|
+
<div aria-hidden="true" inert [style]="measureLayerStyle()">
|
|
794
|
+
@for (item of measured(); track item.key) {
|
|
795
|
+
<span
|
|
796
|
+
[epdfToolbarLength]="item.key"
|
|
797
|
+
[ifDrawn]="item.entry.kind === 'custom'"
|
|
798
|
+
style="display: inline-flex; flex-shrink: 0"
|
|
799
|
+
>
|
|
800
|
+
<ng-container *ngTemplateOutlet="drawn; context: { $implicit: item.entry }" />
|
|
801
|
+
</span>
|
|
802
|
+
}
|
|
803
|
+
<span [epdfToolbarLength]="overflowTriggerKey" style="display: inline-flex; flex-shrink: 0">
|
|
804
|
+
<ng-container
|
|
805
|
+
*ngTemplateOutlet="
|
|
806
|
+
overflowTriggerTemplate()?.template ?? defaultOverflowTrigger;
|
|
807
|
+
context: closedTrigger
|
|
808
|
+
"
|
|
809
|
+
/>
|
|
810
|
+
</span>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
<!--
|
|
814
|
+
An item of the live row, in the element that marks it (TOOLBAR_ITEM_ATTRIBUTE in
|
|
815
|
+
@embedpdf/web): its first element that takes focus is the item's stop in the arrow-key
|
|
816
|
+
order. The element adds no box, so the item lays out as the section's own child.
|
|
817
|
+
-->
|
|
818
|
+
<ng-template #liveEntry let-entry>
|
|
819
|
+
@if (entry.kind === 'separator') {
|
|
820
|
+
<ng-container *ngTemplateOutlet="drawn; context: { $implicit: entry }" />
|
|
821
|
+
} @else {
|
|
822
|
+
<span data-epdf-toolbar-item style="display: contents">
|
|
823
|
+
<ng-container *ngTemplateOutlet="drawn; context: { $implicit: entry }" />
|
|
824
|
+
</span>
|
|
825
|
+
}
|
|
826
|
+
</ng-template>
|
|
827
|
+
|
|
828
|
+
<ng-template #drawn let-entry>
|
|
829
|
+
@switch (entry.kind) {
|
|
830
|
+
@case ('command') {
|
|
831
|
+
<ng-container
|
|
832
|
+
*ngTemplateOutlet="
|
|
833
|
+
commandTemplate()?.template ?? defaultCommand;
|
|
834
|
+
context: entry.context
|
|
835
|
+
"
|
|
836
|
+
/>
|
|
837
|
+
}
|
|
838
|
+
@case ('custom') {
|
|
839
|
+
<ng-container *ngTemplateOutlet="entry.template; context: entry.context" />
|
|
840
|
+
}
|
|
841
|
+
@case ('socket') {
|
|
842
|
+
<!-- Inside a shadow root, the host's children for this slot are drawn here. -->
|
|
843
|
+
<slot
|
|
844
|
+
[attr.name]="entry.slot"
|
|
845
|
+
[epdfToolbarLength]="entry.key"
|
|
846
|
+
style="display: inline-flex; align-items: center; flex-shrink: 0"
|
|
847
|
+
>
|
|
848
|
+
@if (entry.context) {
|
|
849
|
+
<ng-container
|
|
850
|
+
*ngTemplateOutlet="
|
|
851
|
+
commandTemplate()?.template ?? defaultCommand;
|
|
852
|
+
context: entry.context
|
|
853
|
+
"
|
|
854
|
+
/>
|
|
855
|
+
}
|
|
856
|
+
</slot>
|
|
857
|
+
}
|
|
858
|
+
@case ('collapsed') {
|
|
859
|
+
<ng-container
|
|
860
|
+
*ngTemplateOutlet="
|
|
861
|
+
collapsedTemplate()?.template ?? defaultCollapsed;
|
|
862
|
+
context: entry.context
|
|
863
|
+
"
|
|
864
|
+
/>
|
|
865
|
+
}
|
|
866
|
+
@case ('trigger') {
|
|
867
|
+
<ng-container
|
|
868
|
+
*ngTemplateOutlet="
|
|
869
|
+
groupTriggerTemplate()?.template ?? defaultGroupTrigger;
|
|
870
|
+
context: entry.context
|
|
871
|
+
"
|
|
872
|
+
/>
|
|
873
|
+
}
|
|
874
|
+
@case ('separator') {
|
|
875
|
+
<ng-container *ngTemplateOutlet="separatorTemplate()?.template ?? defaultSeparator" />
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
</ng-template>
|
|
879
|
+
|
|
880
|
+
<!-- The defaults: plain, and meant to be replaced. -->
|
|
881
|
+
<ng-template epdfToolbarCommand #defaultCommand let-command let-run="run">
|
|
882
|
+
<button
|
|
883
|
+
type="button"
|
|
884
|
+
[disabled]="!command.enabled"
|
|
885
|
+
[attr.aria-pressed]="command.active || null"
|
|
886
|
+
[attr.aria-haspopup]="command.menu ? 'menu' : null"
|
|
887
|
+
[title]="command.label"
|
|
888
|
+
[style]="commandStyle"
|
|
889
|
+
[style.background]="command.active ? paint.active : 'transparent'"
|
|
890
|
+
[style.cursor]="command.enabled ? 'pointer' : 'default'"
|
|
891
|
+
[style.opacity]="command.enabled ? 1 : 0.4"
|
|
892
|
+
(click)="run()"
|
|
893
|
+
>
|
|
894
|
+
<!-- Icons are the app's: the plain button shows the label in every variant. -->
|
|
895
|
+
{{ command.label }}
|
|
896
|
+
</button>
|
|
897
|
+
</ng-template>
|
|
898
|
+
<ng-template #defaultSeparator>
|
|
899
|
+
<span [style]="separatorStyle()"></span>
|
|
900
|
+
</ng-template>
|
|
901
|
+
<ng-template epdfToolbarOverflowTrigger #defaultOverflowTrigger let-open let-toggle="toggle">
|
|
902
|
+
<epdf-toolbar-more-button [open]="open" (toggle)="toggle()" />
|
|
903
|
+
</ng-template>
|
|
904
|
+
<ng-template epdfToolbarOverflowMenu #defaultOverflowMenu let-view>
|
|
905
|
+
<epdf-toolbar-menu [view]="view" [orientation]="orientation()" />
|
|
906
|
+
</ng-template>
|
|
907
|
+
<ng-template epdfToolbarCollapsed #defaultCollapsed let-view>
|
|
908
|
+
<epdf-toolbar-collapsed [view]="view" [orientation]="orientation()" />
|
|
909
|
+
</ng-template>
|
|
910
|
+
<ng-template epdfToolbarGroupTrigger #defaultGroupTrigger let-view>
|
|
911
|
+
<epdf-toolbar-group-trigger [view]="view" [orientation]="orientation()" />
|
|
912
|
+
</ng-template>
|
|
913
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: EpdfToolbarLength, selector: "[epdfToolbarLength]", inputs: ["epdfToolbarLength", "ifDrawn"] }, { kind: "component", type: EpdfToolbarMoreButton, selector: "epdf-toolbar-more-button", inputs: ["open"], outputs: ["toggle"] }, { kind: "component", type: EpdfToolbarMenu, selector: "epdf-toolbar-menu", inputs: ["view", "orientation"] }, { kind: "component", type: EpdfToolbarCollapsed, selector: "epdf-toolbar-collapsed", inputs: ["view", "orientation"] }, { kind: "component", type: EpdfToolbarGroupTrigger, selector: "epdf-toolbar-group-trigger", inputs: ["view", "orientation"] }, { kind: "directive", type: EpdfToolbarCommandTemplate, selector: "ng-template[epdfToolbarCommand]" }, { kind: "directive", type: EpdfToolbarCollapsedTemplate, selector: "ng-template[epdfToolbarCollapsed]" }, { kind: "directive", type: EpdfToolbarGroupTriggerTemplate, selector: "ng-template[epdfToolbarGroupTrigger]" }, { kind: "directive", type: EpdfToolbarOverflowTriggerTemplate, selector: "ng-template[epdfToolbarOverflowTrigger]" }, { kind: "directive", type: EpdfToolbarOverflowMenuTemplate, selector: "ng-template[epdfToolbarOverflowMenu]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
914
|
+
}
|
|
915
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfToolbar, decorators: [{
|
|
916
|
+
type: Component,
|
|
917
|
+
args: [{
|
|
918
|
+
selector: 'epdf-toolbar',
|
|
919
|
+
imports: [
|
|
920
|
+
NgTemplateOutlet,
|
|
921
|
+
EpdfToolbarLength,
|
|
922
|
+
EpdfToolbarMoreButton,
|
|
923
|
+
EpdfToolbarMenu,
|
|
924
|
+
EpdfToolbarCollapsed,
|
|
925
|
+
EpdfToolbarGroupTrigger,
|
|
926
|
+
EpdfToolbarCommandTemplate,
|
|
927
|
+
EpdfToolbarCollapsedTemplate,
|
|
928
|
+
EpdfToolbarGroupTriggerTemplate,
|
|
929
|
+
EpdfToolbarOverflowTriggerTemplate,
|
|
930
|
+
EpdfToolbarOverflowMenuTemplate,
|
|
931
|
+
],
|
|
932
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
933
|
+
providers: [{ provide: TOOLBAR_LENGTHS, useExisting: forwardRef(() => EpdfToolbar) }],
|
|
934
|
+
host: {
|
|
935
|
+
role: 'toolbar',
|
|
936
|
+
'[attr.aria-orientation]': 'orientation()',
|
|
937
|
+
style: 'position: relative; display: flex; align-items: center;',
|
|
938
|
+
'[style.flex-direction]': "orientation() === 'vertical' ? 'column' : 'row'",
|
|
939
|
+
},
|
|
940
|
+
template: `
|
|
941
|
+
<!--
|
|
942
|
+
The sections run along the toolbar: the center one has auto margins, so it stays centered
|
|
943
|
+
in the space the others leave and gives way before anything overlaps. The sections never
|
|
944
|
+
grow or shrink: what fits is the solver's decision, so the row's least length is exactly
|
|
945
|
+
what it counted.
|
|
946
|
+
-->
|
|
947
|
+
<div [style]="sectionStyle()" [style.gap.px]="gap()">
|
|
948
|
+
@for (entry of row().start; track entry.key) {
|
|
949
|
+
<ng-container *ngTemplateOutlet="liveEntry; context: { $implicit: entry }" />
|
|
950
|
+
}
|
|
951
|
+
</div>
|
|
952
|
+
<div [style]="centerStyle()" [style.gap.px]="gap()">
|
|
953
|
+
@for (entry of row().center; track entry.key) {
|
|
954
|
+
<ng-container *ngTemplateOutlet="liveEntry; context: { $implicit: entry }" />
|
|
955
|
+
}
|
|
956
|
+
</div>
|
|
957
|
+
<div [style]="sectionStyle()" [style.gap.px]="gap()">
|
|
958
|
+
@for (entry of row().end; track entry.key) {
|
|
959
|
+
<ng-container *ngTemplateOutlet="liveEntry; context: { $implicit: entry }" />
|
|
960
|
+
}
|
|
961
|
+
@if (hasOverflow()) {
|
|
962
|
+
<span data-epdf-toolbar-item style="position: relative; display: inline-flex">
|
|
963
|
+
<ng-container
|
|
964
|
+
*ngTemplateOutlet="
|
|
965
|
+
overflowTriggerTemplate()?.template ?? defaultOverflowTrigger;
|
|
966
|
+
context: overflowTrigger()
|
|
967
|
+
"
|
|
968
|
+
/>
|
|
969
|
+
<ng-container
|
|
970
|
+
*ngTemplateOutlet="
|
|
971
|
+
overflowMenuTemplate()?.template ?? defaultOverflowMenu;
|
|
972
|
+
context: overflowMenu()
|
|
973
|
+
"
|
|
974
|
+
/>
|
|
975
|
+
</span>
|
|
976
|
+
}
|
|
977
|
+
</div>
|
|
978
|
+
|
|
979
|
+
<!--
|
|
980
|
+
The measurement layer: every item in every variant, every folded group and the "More"
|
|
981
|
+
button, hidden, inert and observed. Text reflow (a language, fonts, zoom) re-measures by
|
|
982
|
+
itself. An item of your own whose template draws nothing here is measured where it renders.
|
|
983
|
+
-->
|
|
984
|
+
<div aria-hidden="true" inert [style]="measureLayerStyle()">
|
|
985
|
+
@for (item of measured(); track item.key) {
|
|
986
|
+
<span
|
|
987
|
+
[epdfToolbarLength]="item.key"
|
|
988
|
+
[ifDrawn]="item.entry.kind === 'custom'"
|
|
989
|
+
style="display: inline-flex; flex-shrink: 0"
|
|
990
|
+
>
|
|
991
|
+
<ng-container *ngTemplateOutlet="drawn; context: { $implicit: item.entry }" />
|
|
992
|
+
</span>
|
|
993
|
+
}
|
|
994
|
+
<span [epdfToolbarLength]="overflowTriggerKey" style="display: inline-flex; flex-shrink: 0">
|
|
995
|
+
<ng-container
|
|
996
|
+
*ngTemplateOutlet="
|
|
997
|
+
overflowTriggerTemplate()?.template ?? defaultOverflowTrigger;
|
|
998
|
+
context: closedTrigger
|
|
999
|
+
"
|
|
1000
|
+
/>
|
|
1001
|
+
</span>
|
|
1002
|
+
</div>
|
|
1003
|
+
|
|
1004
|
+
<!--
|
|
1005
|
+
An item of the live row, in the element that marks it (TOOLBAR_ITEM_ATTRIBUTE in
|
|
1006
|
+
@embedpdf/web): its first element that takes focus is the item's stop in the arrow-key
|
|
1007
|
+
order. The element adds no box, so the item lays out as the section's own child.
|
|
1008
|
+
-->
|
|
1009
|
+
<ng-template #liveEntry let-entry>
|
|
1010
|
+
@if (entry.kind === 'separator') {
|
|
1011
|
+
<ng-container *ngTemplateOutlet="drawn; context: { $implicit: entry }" />
|
|
1012
|
+
} @else {
|
|
1013
|
+
<span data-epdf-toolbar-item style="display: contents">
|
|
1014
|
+
<ng-container *ngTemplateOutlet="drawn; context: { $implicit: entry }" />
|
|
1015
|
+
</span>
|
|
1016
|
+
}
|
|
1017
|
+
</ng-template>
|
|
1018
|
+
|
|
1019
|
+
<ng-template #drawn let-entry>
|
|
1020
|
+
@switch (entry.kind) {
|
|
1021
|
+
@case ('command') {
|
|
1022
|
+
<ng-container
|
|
1023
|
+
*ngTemplateOutlet="
|
|
1024
|
+
commandTemplate()?.template ?? defaultCommand;
|
|
1025
|
+
context: entry.context
|
|
1026
|
+
"
|
|
1027
|
+
/>
|
|
1028
|
+
}
|
|
1029
|
+
@case ('custom') {
|
|
1030
|
+
<ng-container *ngTemplateOutlet="entry.template; context: entry.context" />
|
|
1031
|
+
}
|
|
1032
|
+
@case ('socket') {
|
|
1033
|
+
<!-- Inside a shadow root, the host's children for this slot are drawn here. -->
|
|
1034
|
+
<slot
|
|
1035
|
+
[attr.name]="entry.slot"
|
|
1036
|
+
[epdfToolbarLength]="entry.key"
|
|
1037
|
+
style="display: inline-flex; align-items: center; flex-shrink: 0"
|
|
1038
|
+
>
|
|
1039
|
+
@if (entry.context) {
|
|
1040
|
+
<ng-container
|
|
1041
|
+
*ngTemplateOutlet="
|
|
1042
|
+
commandTemplate()?.template ?? defaultCommand;
|
|
1043
|
+
context: entry.context
|
|
1044
|
+
"
|
|
1045
|
+
/>
|
|
1046
|
+
}
|
|
1047
|
+
</slot>
|
|
1048
|
+
}
|
|
1049
|
+
@case ('collapsed') {
|
|
1050
|
+
<ng-container
|
|
1051
|
+
*ngTemplateOutlet="
|
|
1052
|
+
collapsedTemplate()?.template ?? defaultCollapsed;
|
|
1053
|
+
context: entry.context
|
|
1054
|
+
"
|
|
1055
|
+
/>
|
|
1056
|
+
}
|
|
1057
|
+
@case ('trigger') {
|
|
1058
|
+
<ng-container
|
|
1059
|
+
*ngTemplateOutlet="
|
|
1060
|
+
groupTriggerTemplate()?.template ?? defaultGroupTrigger;
|
|
1061
|
+
context: entry.context
|
|
1062
|
+
"
|
|
1063
|
+
/>
|
|
1064
|
+
}
|
|
1065
|
+
@case ('separator') {
|
|
1066
|
+
<ng-container *ngTemplateOutlet="separatorTemplate()?.template ?? defaultSeparator" />
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
</ng-template>
|
|
1070
|
+
|
|
1071
|
+
<!-- The defaults: plain, and meant to be replaced. -->
|
|
1072
|
+
<ng-template epdfToolbarCommand #defaultCommand let-command let-run="run">
|
|
1073
|
+
<button
|
|
1074
|
+
type="button"
|
|
1075
|
+
[disabled]="!command.enabled"
|
|
1076
|
+
[attr.aria-pressed]="command.active || null"
|
|
1077
|
+
[attr.aria-haspopup]="command.menu ? 'menu' : null"
|
|
1078
|
+
[title]="command.label"
|
|
1079
|
+
[style]="commandStyle"
|
|
1080
|
+
[style.background]="command.active ? paint.active : 'transparent'"
|
|
1081
|
+
[style.cursor]="command.enabled ? 'pointer' : 'default'"
|
|
1082
|
+
[style.opacity]="command.enabled ? 1 : 0.4"
|
|
1083
|
+
(click)="run()"
|
|
1084
|
+
>
|
|
1085
|
+
<!-- Icons are the app's: the plain button shows the label in every variant. -->
|
|
1086
|
+
{{ command.label }}
|
|
1087
|
+
</button>
|
|
1088
|
+
</ng-template>
|
|
1089
|
+
<ng-template #defaultSeparator>
|
|
1090
|
+
<span [style]="separatorStyle()"></span>
|
|
1091
|
+
</ng-template>
|
|
1092
|
+
<ng-template epdfToolbarOverflowTrigger #defaultOverflowTrigger let-open let-toggle="toggle">
|
|
1093
|
+
<epdf-toolbar-more-button [open]="open" (toggle)="toggle()" />
|
|
1094
|
+
</ng-template>
|
|
1095
|
+
<ng-template epdfToolbarOverflowMenu #defaultOverflowMenu let-view>
|
|
1096
|
+
<epdf-toolbar-menu [view]="view" [orientation]="orientation()" />
|
|
1097
|
+
</ng-template>
|
|
1098
|
+
<ng-template epdfToolbarCollapsed #defaultCollapsed let-view>
|
|
1099
|
+
<epdf-toolbar-collapsed [view]="view" [orientation]="orientation()" />
|
|
1100
|
+
</ng-template>
|
|
1101
|
+
<ng-template epdfToolbarGroupTrigger #defaultGroupTrigger let-view>
|
|
1102
|
+
<epdf-toolbar-group-trigger [view]="view" [orientation]="orientation()" />
|
|
1103
|
+
</ng-template>
|
|
1104
|
+
`,
|
|
1105
|
+
}]
|
|
1106
|
+
}], ctorParameters: () => [] });
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* @embedpdf/angular/toolbar: a toolbar that fits its width (or its height, standing up), with
|
|
1110
|
+
* every part yours.
|
|
1111
|
+
*
|
|
1112
|
+
* <epdf-toolbar [bar] [orientation]> measures, folds, fills a "More" menu, arrow keys
|
|
1113
|
+
* <ng-template epdfToolbarCommand>, … the parts you draw (templates.ts)
|
|
1114
|
+
* group(), item(), custom() the bar's vocabulary, from @embedpdf/core-ui
|
|
1115
|
+
*/
|
|
1116
|
+
// The toolbar's vocabulary: the schema a bar is written in.
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Generated bundle index. Do not edit.
|
|
1120
|
+
*/
|
|
1121
|
+
|
|
1122
|
+
export { EpdfToolbar, EpdfToolbarCollapsedTemplate, EpdfToolbarCommandTemplate, EpdfToolbarCustomTemplate, EpdfToolbarGroupTriggerTemplate, EpdfToolbarOverflowMenuTemplate, EpdfToolbarOverflowTriggerTemplate, EpdfToolbarSeparatorTemplate };
|
|
1123
|
+
//# sourceMappingURL=embedpdf-angular-toolbar.mjs.map
|