@dereekb/dbx-web 13.33.0 → 13.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint/package.json +4 -4
- package/fesm2022/dereekb-dbx-web.mjs +1550 -53
- package/fesm2022/dereekb-dbx-web.mjs.map +1 -1
- package/lib/extension/pdf/_pdf.scss +160 -1
- package/package.json +7 -7
- package/types/dereekb-dbx-web.d.ts +963 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, input, inject, effect, Directive, NgModule, ChangeDetectionStrategy, Component, output, computed, HostListener, forwardRef, model, InjectionToken, viewChild, ElementRef, makeEnvironmentProviders, untracked, ApplicationRef, Injector, EnvironmentInjector, createComponent, DestroyRef, signal, TemplateRef, SecurityContext, ViewContainerRef, booleanAttribute, Renderer2 } from '@angular/core';
|
|
3
|
-
import { asPromise, DASH_CHARACTER_PREFIX_INSTANCE, hashStringToNumber, cssTokenVar, useIterableOrValue, isDefinedAndNotFalse, spaceSeparatedCssClasses, cssClassesSet, objectHasNoKeys, getValueFromGetter, firstValue, filterUndefinedValues, separateValues, splitFront, asDecisionFunction, SLASH_PATH_FILE_TYPE_SEPARATOR, filterMaybeArrayValues, mapIterable, toReadableError, isDefaultReadableError, MS_IN_SECOND, mergeObjects, build, ServerErrorResponse, UnauthorizedServerErrorResponse, makeTimer, MS_IN_MINUTE, toggleTimerRunning, unixDateTimeSecondsNumberForNow, ModelRelationUtility, encodeModelKeyTypePair, safeCompareEquality, addMilliseconds, isPast, asArray, slashPathDetails, mimeTypeForFileExtension, slashPathDirectoryTree, nameToInitials, isMaybeNot, isNotFalse, modifier, combineMaps, addModifiers, removeModifiers, applyBestFit, findNext, maybeModifierMapToFunction, makeValuesGroupMap, compareWithMappedValuesFunction, invertMaybeBoolean, splitCommaSeparatedStringToSet, ZIP_FILE_MIME_TYPE, cachedGetter, sortByNumberFunction, JPEG_MIME_TYPES, JPEG_MIME_TYPE, PNG_MIME_TYPE, PDF_MIME_TYPE, sequentialIncrementingNumberStringModelIdFactory, PDF_HEADER, PDF_EOF_MARKER, PDF_ENCRYPT_MARKER } from '@dereekb/util';
|
|
3
|
+
import { asPromise, DASH_CHARACTER_PREFIX_INSTANCE, hashStringToNumber, cssTokenVar, useIterableOrValue, isDefinedAndNotFalse, spaceSeparatedCssClasses, cssClassesSet, objectHasNoKeys, getValueFromGetter, firstValue, filterUndefinedValues, separateValues, splitFront, asDecisionFunction, SLASH_PATH_FILE_TYPE_SEPARATOR, filterMaybeArrayValues, mapIterable, toReadableError, isDefaultReadableError, MS_IN_SECOND, mergeObjects, build, ServerErrorResponse, UnauthorizedServerErrorResponse, makeTimer, MS_IN_MINUTE, toggleTimerRunning, unixDateTimeSecondsNumberForNow, ModelRelationUtility, encodeModelKeyTypePair, safeCompareEquality, addMilliseconds, isPast, asArray, slashPathDetails, mimeTypeForFileExtension, slashPathDirectoryTree, nameToInitials, isMaybeNot, isNotFalse, modifier, combineMaps, addModifiers, removeModifiers, applyBestFit, findNext, maybeModifierMapToFunction, makeValuesGroupMap, compareWithMappedValuesFunction, invertMaybeBoolean, splitCommaSeparatedStringToSet, ZIP_FILE_MIME_TYPE, cachedGetter, sortByNumberFunction, JPEG_MIME_TYPES, JPEG_MIME_TYPE, PNG_MIME_TYPE, PDF_MIME_TYPE, JSON_MIME_TYPE, sequentialIncrementingNumberStringModelIdFactory, PDF_HEADER, PDF_EOF_MARKER, PDF_ENCRYPT_MARKER } from '@dereekb/util';
|
|
4
4
|
import * as i1$2 from '@dereekb/dbx-core';
|
|
5
5
|
import { completeOnDestroy, clean, AbstractTransitionWatcherDirective, DbxInjectionComponent, dbxActionWorkProgress, AbstractDbxButtonDirective, hasNonTrivialChildNodes, provideDbxButton, DbxCoreButtonModule, createInjectorForInjectionComponentConfig, initInjectionComponent, AbstractDbxActionValueGetterDirective, cleanSubscription, AbstractDbxActionHandlerDirective, FilterSourceDirective, provideActionStoreSource, isClickableFilterPreset, AbstractDbxAnchorDirective, expandClickableAnchorLinkTrees, DbxCoreFilterModule, DbxButton, DbxActionWorkable, provideDbxActionWorkable, DbxActionContextStoreSourceInstance, cleanSubscriptionWithLockSet, DBX_INJECTION_COMPONENT_DATA, checkNgContentWrapperHasContent, cleanLoadingContext, DbxActionSourceDirective, DbxActionSuccessHandlerDirective, DbxActionDirective, transformEmptyStringInputToUndefined, isIdleActionState, canTriggerAction, DbxCoreActionModule, DbxActionButtonDirective, onDbxAppAuth, SimpleStorageAccessorFactory, mergeStaticProviders, asSegueRef, AbstractTransitionDirective, DbxRouterService, AbstractIfDirective, isSegueRefActive, anchorTypeForAnchor } from '@dereekb/dbx-core';
|
|
6
6
|
import { NgPopoverRef, NgOverlayContainerService } from 'ng-overlay-container';
|
|
@@ -52,7 +52,7 @@ import * as i1$8 from '@angular/material/chips';
|
|
|
52
52
|
import { MatChipsModule } from '@angular/material/chips';
|
|
53
53
|
import * as i2$1 from '@angular/material/tooltip';
|
|
54
54
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
55
|
-
import { PDFDocument } from '@cantoo/pdf-lib';
|
|
55
|
+
import { PDFName, PDFString, PDFHexString, PDFDocument, degrees } from '@cantoo/pdf-lib';
|
|
56
56
|
import { moveItemInArray, CdkDrag, CdkDragHandle, CdkDragPlaceholder, CdkDropList } from '@angular/cdk/drag-drop';
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -18151,6 +18151,18 @@ const DEFAULT_DBX_PDF_MERGE_ENCRYPTED_HANDLING = 'focus';
|
|
|
18151
18151
|
* Error message used when an encrypted entry is projected to the `error` status under {@link DbxPdfMergeEncryptedHandling} `'error'` mode.
|
|
18152
18152
|
*/
|
|
18153
18153
|
const DBX_PDF_MERGE_ENCRYPTED_ERROR_MESSAGE = 'Password-protected PDFs cannot be merged.';
|
|
18154
|
+
/**
|
|
18155
|
+
* Default value for {@link DbxPdfMergeEditorConfig.pageEditing}. Off, so the editor's default behavior is unchanged.
|
|
18156
|
+
*/
|
|
18157
|
+
const DEFAULT_DBX_PDF_MERGE_PAGE_EDITING = false;
|
|
18158
|
+
/**
|
|
18159
|
+
* Default value for {@link DbxPdfMergeEditorConfig.sidecar}. Off, so merged output bytes are unchanged.
|
|
18160
|
+
*/
|
|
18161
|
+
const DEFAULT_DBX_PDF_MERGE_SIDECAR = false;
|
|
18162
|
+
/**
|
|
18163
|
+
* Message shown on an encrypted entry's row while page editing is enabled. Encrypted documents cannot be opened by `pdf-lib`, so their pages cannot be listed or edited — the entry stays a single opaque row.
|
|
18164
|
+
*/
|
|
18165
|
+
const DBX_PDF_MERGE_ENCRYPTED_NOT_EDITABLE_MESSAGE = 'Encrypted — pages cannot be edited.';
|
|
18154
18166
|
/**
|
|
18155
18167
|
* MIME types accepted by the PDF merge editor by default: PDF documents and PNG/JPEG images.
|
|
18156
18168
|
*/
|
|
@@ -18159,6 +18171,45 @@ const DEFAULT_PDF_MERGE_ACCEPT = [PDF_MIME_TYPE, PNG_MIME_TYPE, JPEG_MIME_TYPE];
|
|
|
18159
18171
|
* MIME type emitted by the merged result blob.
|
|
18160
18172
|
*/
|
|
18161
18173
|
const PDF_MERGE_RESULT_MIME_TYPE = PDF_MIME_TYPE;
|
|
18174
|
+
/**
|
|
18175
|
+
* Group key used for pages whose source entry has no slot id.
|
|
18176
|
+
*/
|
|
18177
|
+
const DEFAULT_PDF_MERGE_PAGE_GROUP_KEY = '_';
|
|
18178
|
+
/**
|
|
18179
|
+
* Returns the group key a page reorders within — its entry's slot id, or {@link DEFAULT_PDF_MERGE_PAGE_GROUP_KEY} when the entry is unslotted.
|
|
18180
|
+
*
|
|
18181
|
+
* @param slotId - Slot id of the owning entry, if any.
|
|
18182
|
+
* @returns The group key.
|
|
18183
|
+
* @__NO_SIDE_EFFECTS__
|
|
18184
|
+
*/
|
|
18185
|
+
function pdfMergePageGroupKeyForSlotId(slotId) {
|
|
18186
|
+
return slotId ?? DEFAULT_PDF_MERGE_PAGE_GROUP_KEY;
|
|
18187
|
+
}
|
|
18188
|
+
/**
|
|
18189
|
+
* Separator between the entry id and the page index inside a page id.
|
|
18190
|
+
*/
|
|
18191
|
+
const PDF_MERGE_PAGE_ID_SEPARATOR = ':';
|
|
18192
|
+
/**
|
|
18193
|
+
* Builds the stable identifier for a page within the editor.
|
|
18194
|
+
*
|
|
18195
|
+
* @param entryId - Id of the owning {@link PdfMergeEntry}.
|
|
18196
|
+
* @param sourceIndex - Zero-based page index within that entry's source document.
|
|
18197
|
+
* @returns The page id.
|
|
18198
|
+
* @__NO_SIDE_EFFECTS__
|
|
18199
|
+
*/
|
|
18200
|
+
function makePdfMergePageId(entryId, sourceIndex) {
|
|
18201
|
+
return `${entryId}${PDF_MERGE_PAGE_ID_SEPARATOR}${sourceIndex}`;
|
|
18202
|
+
}
|
|
18203
|
+
/**
|
|
18204
|
+
* Returns the id of the entry a page id belongs to. Used to prune page state when an entry leaves the list.
|
|
18205
|
+
*
|
|
18206
|
+
* @param pageId - Page id produced by {@link makePdfMergePageId}.
|
|
18207
|
+
* @returns The owning entry's id.
|
|
18208
|
+
* @__NO_SIDE_EFFECTS__
|
|
18209
|
+
*/
|
|
18210
|
+
function entryIdForPdfMergePageId(pageId) {
|
|
18211
|
+
return pageId.slice(0, pageId.indexOf(PDF_MERGE_PAGE_ID_SEPARATOR));
|
|
18212
|
+
}
|
|
18162
18213
|
/**
|
|
18163
18214
|
* Injection token for a workspace-wide default {@link DbxPdfMergeEditorConfig}. Use {@link provideDbxPdfMergeEditorConfig} to register a value.
|
|
18164
18215
|
*/
|
|
@@ -18186,6 +18237,233 @@ function provideDbxPdfMergeEditorPreserveEntriesOnSlotDestroy(preserve = true) {
|
|
|
18186
18237
|
return { provide: DBX_PDF_MERGE_EDITOR_PRESERVE_ENTRIES_ON_SLOT_DESTROY, useValue: preserve };
|
|
18187
18238
|
}
|
|
18188
18239
|
|
|
18240
|
+
/**
|
|
18241
|
+
* Name of the JSON manifest attached to a merged PDF that records which pages came from which slot.
|
|
18242
|
+
*/
|
|
18243
|
+
const PDF_MERGE_SIDECAR_FILE_NAME = 'dbx-pdf-merge.json';
|
|
18244
|
+
/**
|
|
18245
|
+
* Key set on each output page's dictionary holding that page's {@link PdfMergeSidecarPage.tag}.
|
|
18246
|
+
*
|
|
18247
|
+
* Custom page-dictionary keys travel with the page: they survive `copyPages` (including subsetting and reordering), a save/load round trip, and further edits. That is what makes tags — rather than the manifest's recorded indices — the authoritative record of which document a page belongs to.
|
|
18248
|
+
*/
|
|
18249
|
+
const PDF_MERGE_PAGE_TAG_KEY = 'DbxPageTag';
|
|
18250
|
+
/**
|
|
18251
|
+
* Schema version of the emitted {@link PdfMergeSidecar}.
|
|
18252
|
+
*/
|
|
18253
|
+
const PDF_MERGE_SIDECAR_VERSION = 1;
|
|
18254
|
+
/**
|
|
18255
|
+
* Description recorded on the attached manifest.
|
|
18256
|
+
*/
|
|
18257
|
+
const PDF_MERGE_SIDECAR_DESCRIPTION = 'dbx-pdf-merge page manifest';
|
|
18258
|
+
/**
|
|
18259
|
+
* Builds the tag written onto an output page.
|
|
18260
|
+
*
|
|
18261
|
+
* @param slotId - Slot the page's source entry belongs to, or `null` when unslotted.
|
|
18262
|
+
* @param ordinal - Zero-based position of the page within its own slot's pages.
|
|
18263
|
+
* @returns A tag unique within the output document.
|
|
18264
|
+
* @__NO_SIDE_EFFECTS__
|
|
18265
|
+
*/
|
|
18266
|
+
function makePdfMergeSidecarPageTag(slotId, ordinal) {
|
|
18267
|
+
return `${slotId ?? '_'}-${ordinal}`;
|
|
18268
|
+
}
|
|
18269
|
+
/**
|
|
18270
|
+
* Writes a page's tag onto its page dictionary so the association survives later reordering.
|
|
18271
|
+
*
|
|
18272
|
+
* @param page - Page in the output document.
|
|
18273
|
+
* @param tag - Tag to record.
|
|
18274
|
+
*/
|
|
18275
|
+
function writePdfMergePageTag(page, tag) {
|
|
18276
|
+
page.node.set(PDFName.of(PDF_MERGE_PAGE_TAG_KEY), PDFString.of(tag));
|
|
18277
|
+
}
|
|
18278
|
+
/**
|
|
18279
|
+
* Reads the tag previously written onto a page, if any.
|
|
18280
|
+
*
|
|
18281
|
+
* Uses `get` plus an instance check rather than a typed `lookup`, because `PDFDict.lookup(key, PDFString)` throws when the key is absent — which is the normal case for any PDF that did not come out of this editor.
|
|
18282
|
+
*
|
|
18283
|
+
* @param page - Page to inspect.
|
|
18284
|
+
* @returns The tag, or `null` when the page carries none.
|
|
18285
|
+
*/
|
|
18286
|
+
function readPdfMergePageTag(page) {
|
|
18287
|
+
const value = page.node.get(PDFName.of(PDF_MERGE_PAGE_TAG_KEY));
|
|
18288
|
+
let tag;
|
|
18289
|
+
if (value instanceof PDFString || value instanceof PDFHexString) {
|
|
18290
|
+
tag = value.decodeText();
|
|
18291
|
+
}
|
|
18292
|
+
else {
|
|
18293
|
+
tag = null;
|
|
18294
|
+
}
|
|
18295
|
+
return tag;
|
|
18296
|
+
}
|
|
18297
|
+
/**
|
|
18298
|
+
* Attaches a manifest to the output document as an embedded JSON file.
|
|
18299
|
+
*
|
|
18300
|
+
* @param document - Output document being saved.
|
|
18301
|
+
* @param sidecar - Manifest to embed.
|
|
18302
|
+
*/
|
|
18303
|
+
async function attachPdfMergeSidecar(document, sidecar) {
|
|
18304
|
+
// Re-wrap with this module's Uint8Array: pdf-lib validates its input with `instanceof`, and a
|
|
18305
|
+
// TextEncoder supplied by another realm (Node's, under jsdom) produces an array that fails that
|
|
18306
|
+
// check even though it is a genuine Uint8Array.
|
|
18307
|
+
const bytes = new Uint8Array(new TextEncoder().encode(JSON.stringify(sidecar)));
|
|
18308
|
+
await document.attach(bytes, PDF_MERGE_SIDECAR_FILE_NAME, {
|
|
18309
|
+
mimeType: JSON_MIME_TYPE,
|
|
18310
|
+
description: PDF_MERGE_SIDECAR_DESCRIPTION
|
|
18311
|
+
});
|
|
18312
|
+
}
|
|
18313
|
+
/**
|
|
18314
|
+
* Builds a {@link PdfMergeSidecar} from the pages written to an output document.
|
|
18315
|
+
*
|
|
18316
|
+
* @param pages - Page records in output order.
|
|
18317
|
+
* @returns The manifest, with one document per distinct slot in first-appearance order.
|
|
18318
|
+
* @__NO_SIDE_EFFECTS__
|
|
18319
|
+
*/
|
|
18320
|
+
function makePdfMergeSidecar(pages) {
|
|
18321
|
+
const tagsBySlot = new Map();
|
|
18322
|
+
pages.forEach((page) => {
|
|
18323
|
+
const existing = tagsBySlot.get(page.slotId);
|
|
18324
|
+
if (existing == null) {
|
|
18325
|
+
tagsBySlot.set(page.slotId, [page.tag]);
|
|
18326
|
+
}
|
|
18327
|
+
else {
|
|
18328
|
+
existing.push(page.tag);
|
|
18329
|
+
}
|
|
18330
|
+
});
|
|
18331
|
+
const documents = Array.from(tagsBySlot.entries()).map(([slotId, pageTags]) => ({ slotId, pageTags }));
|
|
18332
|
+
return {
|
|
18333
|
+
version: PDF_MERGE_SIDECAR_VERSION,
|
|
18334
|
+
createdAt: new Date().toISOString(),
|
|
18335
|
+
documents,
|
|
18336
|
+
pages
|
|
18337
|
+
};
|
|
18338
|
+
}
|
|
18339
|
+
async function bytesForSidecarReadInput(input) {
|
|
18340
|
+
return input instanceof Blob ? input.arrayBuffer() : input;
|
|
18341
|
+
}
|
|
18342
|
+
function parseSidecarBytes(data) {
|
|
18343
|
+
let sidecar;
|
|
18344
|
+
try {
|
|
18345
|
+
const parsed = JSON.parse(new TextDecoder().decode(data));
|
|
18346
|
+
sidecar = Array.isArray(parsed?.documents) && Array.isArray(parsed?.pages) ? parsed : null;
|
|
18347
|
+
}
|
|
18348
|
+
catch {
|
|
18349
|
+
sidecar = null;
|
|
18350
|
+
}
|
|
18351
|
+
return sidecar;
|
|
18352
|
+
}
|
|
18353
|
+
/**
|
|
18354
|
+
* Reads the manifest embedded in a merged PDF and resolves each recorded page to its current position in that file.
|
|
18355
|
+
*
|
|
18356
|
+
* Resolution prefers each page's own tag, which follows the page through reordering. The manifest's {@link PdfMergeSidecarPage.outputIndex} is used only as a fallback for pages whose tag has been stripped, and any index that no longer exists is reported through {@link PdfMergeSidecarReadResult.missingTags} instead.
|
|
18357
|
+
*
|
|
18358
|
+
* @param input - The merged PDF's bytes.
|
|
18359
|
+
* @returns The manifest and its resolved documents, or `null` when the file carries no manifest, is unreadable, or is encrypted.
|
|
18360
|
+
*/
|
|
18361
|
+
async function readPdfMergeSidecar(input) {
|
|
18362
|
+
let result;
|
|
18363
|
+
try {
|
|
18364
|
+
const bytes = await bytesForSidecarReadInput(input);
|
|
18365
|
+
const document = await PDFDocument.load(bytes);
|
|
18366
|
+
result = resolvePdfMergeSidecar(document);
|
|
18367
|
+
}
|
|
18368
|
+
catch {
|
|
18369
|
+
result = null;
|
|
18370
|
+
}
|
|
18371
|
+
return result;
|
|
18372
|
+
}
|
|
18373
|
+
/**
|
|
18374
|
+
* Resolves the manifest of an already-loaded document. Split out from {@link readPdfMergeSidecar} so {@link splitPdfMergeSidecarDocuments} can reuse the resolution without parsing the same bytes twice.
|
|
18375
|
+
*
|
|
18376
|
+
* @param document - Loaded merged document.
|
|
18377
|
+
* @returns The resolved manifest, or `null` when the document carries none.
|
|
18378
|
+
*/
|
|
18379
|
+
function resolvePdfMergeSidecar(document) {
|
|
18380
|
+
let result;
|
|
18381
|
+
{
|
|
18382
|
+
const attachment = document.getAttachments().find((x) => x.name === PDF_MERGE_SIDECAR_FILE_NAME);
|
|
18383
|
+
const sidecar = attachment == null ? null : parseSidecarBytes(attachment.data);
|
|
18384
|
+
if (sidecar == null) {
|
|
18385
|
+
result = null;
|
|
18386
|
+
}
|
|
18387
|
+
else {
|
|
18388
|
+
const pageCount = document.getPageCount();
|
|
18389
|
+
const indexByTag = new Map();
|
|
18390
|
+
let untaggedPageCount = 0;
|
|
18391
|
+
document.getPages().forEach((page, index) => {
|
|
18392
|
+
const tag = readPdfMergePageTag(page);
|
|
18393
|
+
if (tag == null) {
|
|
18394
|
+
untaggedPageCount += 1;
|
|
18395
|
+
}
|
|
18396
|
+
else {
|
|
18397
|
+
indexByTag.set(tag, index);
|
|
18398
|
+
}
|
|
18399
|
+
});
|
|
18400
|
+
const recordByTag = new Map(sidecar.pages.map((page) => [page.tag, page]));
|
|
18401
|
+
const missingTags = [];
|
|
18402
|
+
const documents = sidecar.documents.map((sidecarDocument) => {
|
|
18403
|
+
const pages = [];
|
|
18404
|
+
sidecarDocument.pageTags.forEach((tag) => {
|
|
18405
|
+
const record = recordByTag.get(tag);
|
|
18406
|
+
const taggedIndex = indexByTag.get(tag);
|
|
18407
|
+
if (record == null) {
|
|
18408
|
+
missingTags.push(tag);
|
|
18409
|
+
}
|
|
18410
|
+
else if (taggedIndex != null) {
|
|
18411
|
+
pages.push({ ...record, pageIndex: taggedIndex, resolvedByTag: true });
|
|
18412
|
+
}
|
|
18413
|
+
else if (record.outputIndex >= 0 && record.outputIndex < pageCount) {
|
|
18414
|
+
pages.push({ ...record, pageIndex: record.outputIndex, resolvedByTag: false });
|
|
18415
|
+
}
|
|
18416
|
+
else {
|
|
18417
|
+
missingTags.push(tag);
|
|
18418
|
+
}
|
|
18419
|
+
});
|
|
18420
|
+
return { slotId: sidecarDocument.slotId, pages };
|
|
18421
|
+
});
|
|
18422
|
+
result = { sidecar, documents, missingTags, untaggedPageCount };
|
|
18423
|
+
}
|
|
18424
|
+
}
|
|
18425
|
+
return result;
|
|
18426
|
+
}
|
|
18427
|
+
/**
|
|
18428
|
+
* Splits a previously-merged PDF back into one file per document, using the embedded manifest to decide which pages belong to which slot.
|
|
18429
|
+
*
|
|
18430
|
+
* This is the inverse of a merge with `sidecar: true`, and is what lets a completed document be re-imported into the editor with its slots repopulated instead of arriving as one opaque file. Page order and any rotation baked in at merge time are preserved, and each document's pages are resolved by tag so a document that was reordered after export still splits correctly.
|
|
18431
|
+
*
|
|
18432
|
+
* @param input - Bytes of a merged PDF.
|
|
18433
|
+
* @returns One file per document plus the manifest, or `null` when the input carries no manifest, is unreadable, or is encrypted.
|
|
18434
|
+
*/
|
|
18435
|
+
async function splitPdfMergeSidecarDocuments(input) {
|
|
18436
|
+
let result;
|
|
18437
|
+
try {
|
|
18438
|
+
const bytes = await bytesForSidecarReadInput(input);
|
|
18439
|
+
const source = await PDFDocument.load(bytes);
|
|
18440
|
+
const read = resolvePdfMergeSidecar(source);
|
|
18441
|
+
if (read == null) {
|
|
18442
|
+
result = null;
|
|
18443
|
+
}
|
|
18444
|
+
else {
|
|
18445
|
+
const documents = [];
|
|
18446
|
+
for (const document of read.documents) {
|
|
18447
|
+
if (document.pages.length > 0) {
|
|
18448
|
+
const target = await PDFDocument.create();
|
|
18449
|
+
const copied = await target.copyPages(source, document.pages.map((page) => page.pageIndex));
|
|
18450
|
+
copied.forEach((page) => target.addPage(page));
|
|
18451
|
+
const outputBytes = await target.save();
|
|
18452
|
+
const name = document.pages[0].sourceName || `${document.slotId ?? 'document'}.pdf`;
|
|
18453
|
+
const file = new File([outputBytes], name, { type: PDF_MIME_TYPE });
|
|
18454
|
+
documents.push({ slotId: document.slotId, file, pageCount: document.pages.length });
|
|
18455
|
+
}
|
|
18456
|
+
}
|
|
18457
|
+
result = { sidecar: read.sidecar, documents, missingTags: read.missingTags, untaggedPageCount: read.untaggedPageCount };
|
|
18458
|
+
}
|
|
18459
|
+
}
|
|
18460
|
+
catch {
|
|
18461
|
+
result = null;
|
|
18462
|
+
}
|
|
18463
|
+
return result;
|
|
18464
|
+
}
|
|
18465
|
+
|
|
18466
|
+
const FULL_ROTATION_DEGREES = 360;
|
|
18189
18467
|
const TEXT_DECODER = new TextDecoder('latin1');
|
|
18190
18468
|
const FORMAT_KILOBYTE = 1024;
|
|
18191
18469
|
const FORMAT_MEGABYTE = FORMAT_KILOBYTE * 1024;
|
|
@@ -18209,6 +18487,33 @@ function formatPdfMergeEntrySize(size) {
|
|
|
18209
18487
|
}
|
|
18210
18488
|
return result;
|
|
18211
18489
|
}
|
|
18490
|
+
const DEFAULT_BLOB_FILE_NAMES = {
|
|
18491
|
+
[PDF_MIME_TYPE]: 'document.pdf',
|
|
18492
|
+
[PNG_MIME_TYPE]: 'image.png',
|
|
18493
|
+
[JPEG_MIME_TYPE]: 'image.jpg'
|
|
18494
|
+
};
|
|
18495
|
+
const DEFAULT_BLOB_FILE_NAME = DEFAULT_BLOB_FILE_NAMES[PDF_MIME_TYPE];
|
|
18496
|
+
/**
|
|
18497
|
+
* Normalizes a {@link Blob} into a {@link File} so it can become a {@link PdfMergeEntry}, which needs a name to display.
|
|
18498
|
+
*
|
|
18499
|
+
* A {@link File} passes through untouched unless an explicit `fileName` overrides it. A bare {@link Blob} is named from `fileName`, else from its MIME type (`document.pdf` / `image.png` / `image.jpg`), else `document.pdf` — the name only has to be plausible and correctly suffixed, since {@link classifyPdfMergeFile} falls back to the extension when a blob carries no type.
|
|
18500
|
+
*
|
|
18501
|
+
* @param input - Blob or File to normalize.
|
|
18502
|
+
* @param fileName - Optional name, overriding a File's own name and naming a bare Blob.
|
|
18503
|
+
* @returns A File carrying the resolved name.
|
|
18504
|
+
*/
|
|
18505
|
+
function asPdfMergeFile(input, fileName) {
|
|
18506
|
+
const mimeType = (input.type ?? '').toLowerCase();
|
|
18507
|
+
let file;
|
|
18508
|
+
if (input instanceof File && fileName == null) {
|
|
18509
|
+
file = input;
|
|
18510
|
+
}
|
|
18511
|
+
else {
|
|
18512
|
+
const name = fileName ?? DEFAULT_BLOB_FILE_NAMES[mimeType] ?? DEFAULT_BLOB_FILE_NAME;
|
|
18513
|
+
file = new File([input], name, { type: input.type || mimeTypeForFileExtension(slashPathDetails(name.toLowerCase()).typedFileExtension) || PDF_MIME_TYPE });
|
|
18514
|
+
}
|
|
18515
|
+
return file;
|
|
18516
|
+
}
|
|
18212
18517
|
/**
|
|
18213
18518
|
* Returns the {@link PdfMergeEntryKind} for a file based on its MIME type, with a small fallback to file-extension matching when the browser provided no MIME type.
|
|
18214
18519
|
*
|
|
@@ -18363,30 +18668,305 @@ async function validatePdfMergeEntry(entry) {
|
|
|
18363
18668
|
}
|
|
18364
18669
|
return result;
|
|
18365
18670
|
}
|
|
18366
|
-
|
|
18367
|
-
|
|
18368
|
-
|
|
18369
|
-
|
|
18370
|
-
|
|
18671
|
+
/**
|
|
18672
|
+
* Normalizes an arbitrary degree value into the `[0, 360)` range.
|
|
18673
|
+
*
|
|
18674
|
+
* @param angle - Angle in degrees, possibly negative or over a full turn.
|
|
18675
|
+
* @returns The equivalent angle in `[0, 360)`.
|
|
18676
|
+
* @__NO_SIDE_EFFECTS__
|
|
18677
|
+
*/
|
|
18678
|
+
function normalizeRotationDegrees(angle) {
|
|
18679
|
+
return ((angle % FULL_ROTATION_DEGREES) + FULL_ROTATION_DEGREES) % FULL_ROTATION_DEGREES;
|
|
18680
|
+
}
|
|
18681
|
+
/**
|
|
18682
|
+
* Reads per-page metadata from an entry's source document so the editor can list its pages individually.
|
|
18683
|
+
*
|
|
18684
|
+
* Only called while page editing is enabled — the normal upload path never parses the document, it only scans for the PDF header and markers (see {@link validatePdfMergeEntry}).
|
|
18685
|
+
*
|
|
18686
|
+
* @param entry - Entry to inspect.
|
|
18687
|
+
* @returns One {@link PdfMergePageMeta} per source page, or `null` when the entry is encrypted or cannot be parsed. Image entries report a single synthetic page.
|
|
18688
|
+
*/
|
|
18689
|
+
async function readPdfMergeEntryPageMetas(entry) {
|
|
18690
|
+
let metas;
|
|
18691
|
+
if (entry.encrypted) {
|
|
18692
|
+
// pdf-lib cannot open an encrypted document at all, so its pages can never be listed or edited.
|
|
18693
|
+
metas = null;
|
|
18694
|
+
}
|
|
18695
|
+
else if (entry.kind === 'image') {
|
|
18696
|
+
const dimensions = entry.original.dimensions;
|
|
18697
|
+
metas = [{ sourceIndex: 0, width: dimensions?.width ?? 0, height: dimensions?.height ?? 0, sourceRotation: 0 }];
|
|
18698
|
+
}
|
|
18699
|
+
else {
|
|
18700
|
+
try {
|
|
18701
|
+
const bytes = await entry.file.arrayBuffer();
|
|
18702
|
+
const source = await PDFDocument.load(bytes);
|
|
18703
|
+
metas = source.getPages().map((page, sourceIndex) => {
|
|
18704
|
+
const { width, height } = page.getSize();
|
|
18705
|
+
return { sourceIndex, width, height, sourceRotation: normalizeRotationDegrees(page.getRotation().angle) };
|
|
18706
|
+
});
|
|
18707
|
+
}
|
|
18708
|
+
catch {
|
|
18709
|
+
metas = null;
|
|
18710
|
+
}
|
|
18711
|
+
}
|
|
18712
|
+
return metas;
|
|
18713
|
+
}
|
|
18714
|
+
/**
|
|
18715
|
+
* Reconstructs editor entries from a PDF previously exported with an embedded manifest.
|
|
18716
|
+
*
|
|
18717
|
+
* Each document recorded in the manifest becomes one entry carrying its original slot id, so dropping the result into the store repopulates the same slots the file was built from. This is the re-import half of the sidecar: without it a completed document can only come back as a single opaque file.
|
|
18718
|
+
*
|
|
18719
|
+
* @param input - Bytes of a merged PDF, typically the file the user just chose.
|
|
18720
|
+
* @param config - Optional entry-building config (id factory override, and the sidecar-less fallback).
|
|
18721
|
+
* @returns The reconstructed entries and manifest, or a reason when the file cannot be imported.
|
|
18722
|
+
*/
|
|
18723
|
+
async function buildPdfMergeEntriesFromSidecar(input, config) {
|
|
18724
|
+
const split = await splitPdfMergeSidecarDocuments(input);
|
|
18725
|
+
let result;
|
|
18726
|
+
if (split == null) {
|
|
18727
|
+
// A readable PDF without a manifest and an unreadable one are distinguished so the UI can
|
|
18728
|
+
// explain the difference — "not exported from here" reads very differently from "corrupt".
|
|
18729
|
+
const readable = await isReadablePdf(input);
|
|
18730
|
+
if (!readable) {
|
|
18731
|
+
result = { error: 'unreadable' };
|
|
18732
|
+
}
|
|
18733
|
+
else if (config?.allowWithoutSidecar) {
|
|
18734
|
+
// No manifest to attribute pages with, so the whole file becomes one unslotted entry. An
|
|
18735
|
+
// `unreadable` file never reaches here — the fallback widens which readable PDFs are
|
|
18736
|
+
// accepted, it does not accept broken ones.
|
|
18737
|
+
const entry = buildPdfMergeEntrySync(asPdfMergeFile(input, config?.fileName), { ...config, slotId: null });
|
|
18738
|
+
result = entry == null ? { error: 'unreadable' } : { entries: [entry], sidecar: null, fromSidecar: false, slotIds: [null], missingTags: [], untaggedPageCount: 0 };
|
|
18739
|
+
}
|
|
18740
|
+
else {
|
|
18741
|
+
result = { error: 'no_sidecar' };
|
|
18742
|
+
}
|
|
18743
|
+
}
|
|
18744
|
+
else if (split.documents.length === 0) {
|
|
18745
|
+
result = { error: 'no_documents' };
|
|
18746
|
+
}
|
|
18747
|
+
else {
|
|
18748
|
+
const entries = split.documents.map((document) => buildPdfMergeEntrySync(document.file, { ...config, slotId: document.slotId })).filter((entry) => entry != null);
|
|
18749
|
+
result = {
|
|
18750
|
+
entries,
|
|
18751
|
+
sidecar: split.sidecar,
|
|
18752
|
+
fromSidecar: true,
|
|
18753
|
+
slotIds: split.documents.map((document) => document.slotId),
|
|
18754
|
+
missingTags: split.missingTags,
|
|
18755
|
+
untaggedPageCount: split.untaggedPageCount
|
|
18756
|
+
};
|
|
18757
|
+
}
|
|
18758
|
+
return result;
|
|
18759
|
+
}
|
|
18760
|
+
async function isReadablePdf(input) {
|
|
18761
|
+
let readable;
|
|
18762
|
+
try {
|
|
18763
|
+
await PDFDocument.load(await input.arrayBuffer());
|
|
18764
|
+
readable = true;
|
|
18765
|
+
}
|
|
18766
|
+
catch {
|
|
18767
|
+
readable = false;
|
|
18768
|
+
}
|
|
18769
|
+
return readable;
|
|
18770
|
+
}
|
|
18771
|
+
function applyPdfMergePageGroupOrder(natural, orderedIds) {
|
|
18772
|
+
let ordered;
|
|
18773
|
+
if (orderedIds == null || orderedIds.length === 0) {
|
|
18774
|
+
ordered = [...natural];
|
|
18775
|
+
}
|
|
18776
|
+
else {
|
|
18777
|
+
const byId = new Map(natural.map((page) => [page.id, page]));
|
|
18778
|
+
const taken = new Set();
|
|
18779
|
+
const result = [];
|
|
18780
|
+
// Stored ids that still resolve keep their stored order; anything else is ignored.
|
|
18781
|
+
orderedIds.forEach((id) => {
|
|
18782
|
+
const page = byId.get(id);
|
|
18783
|
+
if (page != null && !taken.has(id)) {
|
|
18784
|
+
result.push(page);
|
|
18785
|
+
taken.add(id);
|
|
18786
|
+
}
|
|
18787
|
+
});
|
|
18788
|
+
// Pages the stored order does not mention are new since it was written — append them in natural order.
|
|
18789
|
+
natural.forEach((page) => {
|
|
18790
|
+
if (!taken.has(page.id)) {
|
|
18791
|
+
result.push(page);
|
|
18792
|
+
}
|
|
18793
|
+
});
|
|
18794
|
+
ordered = result;
|
|
18795
|
+
}
|
|
18796
|
+
return ordered;
|
|
18797
|
+
}
|
|
18798
|
+
/**
|
|
18799
|
+
* Builds the editor's page plan from the current entries plus the user's sparse edits.
|
|
18800
|
+
*
|
|
18801
|
+
* The result is self-healing: stored ordering ids that no longer resolve are dropped, pages the stored ordering does not mention are appended in natural order, and overrides for departed pages are ignored. That means entries can be added and removed without any page-level bookkeeping.
|
|
18802
|
+
*
|
|
18803
|
+
* Entries whose metadata is `null` contribute no pages — they are encrypted (which `pdf-lib` cannot open) or unparseable. Callers should surface those separately rather than letting them disappear silently.
|
|
18804
|
+
*
|
|
18805
|
+
* @param input - Entries, hydrated metadata, and the stored edits.
|
|
18806
|
+
* @returns The ordered page plan across every group.
|
|
18807
|
+
* @__NO_SIDE_EFFECTS__
|
|
18808
|
+
*/
|
|
18809
|
+
function buildPdfMergePagePlan(input) {
|
|
18810
|
+
const { entries, pageMetas, pageOverrides, pageOrder } = input;
|
|
18811
|
+
const eligible = entries.filter((entry) => entry.status === 'ready' && !entry.ignored && pageMetas[entry.id] != null);
|
|
18812
|
+
const groups = makeValuesGroupMap(eligible, (entry) => pdfMergePageGroupKeyForSlotId(entry.slotId));
|
|
18813
|
+
const plan = [];
|
|
18814
|
+
groups.forEach((groupEntries, groupKey) => {
|
|
18815
|
+
const natural = [];
|
|
18816
|
+
groupEntries.forEach((entry) => {
|
|
18817
|
+
const metas = pageMetas[entry.id];
|
|
18818
|
+
metas.forEach((meta) => {
|
|
18819
|
+
const id = makePdfMergePageId(entry.id, meta.sourceIndex);
|
|
18820
|
+
const override = pageOverrides[id];
|
|
18821
|
+
natural.push({
|
|
18822
|
+
id,
|
|
18823
|
+
entryId: entry.id,
|
|
18824
|
+
slotId: entry.slotId ?? null,
|
|
18825
|
+
groupKey: groupKey,
|
|
18826
|
+
sourceName: entry.name,
|
|
18827
|
+
kind: entry.kind,
|
|
18828
|
+
sourceIndex: meta.sourceIndex,
|
|
18829
|
+
sourcePageCount: metas.length,
|
|
18830
|
+
meta,
|
|
18831
|
+
rotation: override?.rotation ?? 0,
|
|
18832
|
+
removed: override?.removed ?? false
|
|
18833
|
+
});
|
|
18834
|
+
});
|
|
18835
|
+
});
|
|
18836
|
+
applyPdfMergePageGroupOrder(natural, pageOrder[groupKey]).forEach((page) => plan.push(page));
|
|
18837
|
+
});
|
|
18838
|
+
return plan;
|
|
18371
18839
|
}
|
|
18372
|
-
|
|
18840
|
+
/**
|
|
18841
|
+
* Returns a loader that parses each entry's source document at most once per merge. A page plan routinely pulls several non-adjacent pages from the same file, so without this the same bytes would be parsed repeatedly.
|
|
18842
|
+
*
|
|
18843
|
+
* @returns Loader that resolves an entry to its parsed source document.
|
|
18844
|
+
*/
|
|
18845
|
+
function makePdfSourceDocumentLoader() {
|
|
18846
|
+
const cache = new Map();
|
|
18847
|
+
return (entry) => {
|
|
18848
|
+
let loaded = cache.get(entry.id);
|
|
18849
|
+
if (loaded == null) {
|
|
18850
|
+
loaded = entry.file.arrayBuffer().then((bytes) => PDFDocument.load(bytes));
|
|
18851
|
+
cache.set(entry.id, loaded);
|
|
18852
|
+
}
|
|
18853
|
+
return loaded;
|
|
18854
|
+
};
|
|
18855
|
+
}
|
|
18856
|
+
async function appendImagePage(target, entry, rotation) {
|
|
18373
18857
|
const bytes = await entry.file.arrayBuffer();
|
|
18374
18858
|
const isPng = entry.mimeType === PNG_MIME_TYPE || entry.name.toLowerCase().endsWith('.png');
|
|
18375
18859
|
const image = isPng ? await target.embedPng(bytes) : await target.embedJpg(bytes);
|
|
18376
18860
|
const page = target.addPage([image.width, image.height]);
|
|
18377
18861
|
page.drawImage(image, { x: 0, y: 0, width: image.width, height: image.height });
|
|
18862
|
+
if (rotation !== 0) {
|
|
18863
|
+
page.setRotation(degrees(rotation));
|
|
18864
|
+
}
|
|
18865
|
+
return { entry, sourceIndex: 0, rotation };
|
|
18866
|
+
}
|
|
18867
|
+
/**
|
|
18868
|
+
* Appends every page of every entry, in entry order — the behavior used when no page plan is supplied.
|
|
18869
|
+
*
|
|
18870
|
+
* @param input - Target document, entries, and source loader.
|
|
18871
|
+
* @returns One record per appended page, aligned with the target's pages.
|
|
18872
|
+
*/
|
|
18873
|
+
async function appendAllEntryPages(input) {
|
|
18874
|
+
const { target, entries, loadSource } = input;
|
|
18875
|
+
const records = [];
|
|
18876
|
+
for (const entry of entries) {
|
|
18877
|
+
if (entry.kind === 'pdf') {
|
|
18878
|
+
const source = await loadSource(entry);
|
|
18879
|
+
const indices = source.getPageIndices();
|
|
18880
|
+
const copied = await target.copyPages(source, indices);
|
|
18881
|
+
copied.forEach((page, index) => {
|
|
18882
|
+
target.addPage(page);
|
|
18883
|
+
records.push({ entry, sourceIndex: indices[index], rotation: normalizeRotationDegrees(page.getRotation().angle) });
|
|
18884
|
+
});
|
|
18885
|
+
}
|
|
18886
|
+
else {
|
|
18887
|
+
records.push(await appendImagePage(target, entry, 0));
|
|
18888
|
+
}
|
|
18889
|
+
}
|
|
18890
|
+
return records;
|
|
18891
|
+
}
|
|
18892
|
+
/**
|
|
18893
|
+
* Appends pages according to an explicit plan — honoring subsetting, reordering, and per-page rotation.
|
|
18894
|
+
*
|
|
18895
|
+
* Consecutive pages from the same entry are batched into a single `copyPages` call. `copyPages` returns pages in the order of the indices it is given, which is what lets subsetting and reordering happen in one operation.
|
|
18896
|
+
*
|
|
18897
|
+
* @param input - Target document, entries, page plan, and source loader.
|
|
18898
|
+
* @returns One record per appended page, aligned with the target's pages.
|
|
18899
|
+
*/
|
|
18900
|
+
async function appendPlannedPages(input) {
|
|
18901
|
+
const { target, entries, plan, loadSource } = input;
|
|
18902
|
+
const entriesById = new Map(entries.map((entry) => [entry.id, entry]));
|
|
18903
|
+
const visible = plan.filter((page) => !page.removed && entriesById.has(page.entryId));
|
|
18904
|
+
const records = [];
|
|
18905
|
+
let index = 0;
|
|
18906
|
+
while (index < visible.length) {
|
|
18907
|
+
const first = visible[index];
|
|
18908
|
+
const entry = entriesById.get(first.entryId);
|
|
18909
|
+
let end = index + 1;
|
|
18910
|
+
while (end < visible.length && visible[end].entryId === first.entryId) {
|
|
18911
|
+
end += 1;
|
|
18912
|
+
}
|
|
18913
|
+
const run = visible.slice(index, end);
|
|
18914
|
+
if (entry.kind === 'image') {
|
|
18915
|
+
for (const planned of run) {
|
|
18916
|
+
records.push(await appendImagePage(target, entry, normalizeRotationDegrees(planned.rotation)));
|
|
18917
|
+
}
|
|
18918
|
+
}
|
|
18919
|
+
else {
|
|
18920
|
+
const source = await loadSource(entry);
|
|
18921
|
+
const copied = await target.copyPages(source, run.map((planned) => planned.sourceIndex));
|
|
18922
|
+
copied.forEach((page, runIndex) => {
|
|
18923
|
+
const planned = run[runIndex];
|
|
18924
|
+
// Compose rather than replace: a scan already stored at 90° that the user rotates by 90° must land at 180°.
|
|
18925
|
+
const rotation = normalizeRotationDegrees(planned.meta.sourceRotation + planned.rotation);
|
|
18926
|
+
target.addPage(page);
|
|
18927
|
+
page.setRotation(degrees(rotation));
|
|
18928
|
+
records.push({ entry, sourceIndex: planned.sourceIndex, rotation });
|
|
18929
|
+
});
|
|
18930
|
+
}
|
|
18931
|
+
index = end;
|
|
18932
|
+
}
|
|
18933
|
+
return records;
|
|
18934
|
+
}
|
|
18935
|
+
/**
|
|
18936
|
+
* Tags each output page and attaches the manifest describing which pages belong to which slot.
|
|
18937
|
+
*
|
|
18938
|
+
* @param target - Document being assembled, whose pages align with `records`.
|
|
18939
|
+
* @param records - One record per appended page, in output order.
|
|
18940
|
+
*/
|
|
18941
|
+
async function writePdfMergeSidecarForRecords(target, records) {
|
|
18942
|
+
const ordinalBySlotKey = new Map();
|
|
18943
|
+
const outputPages = target.getPages();
|
|
18944
|
+
const sidecarPages = records.map((record, outputIndex) => {
|
|
18945
|
+
// Normalize to null so the value survives JSON.stringify, which drops undefined.
|
|
18946
|
+
const slotId = record.entry.slotId ?? null;
|
|
18947
|
+
const slotKey = slotId ?? '';
|
|
18948
|
+
const ordinal = ordinalBySlotKey.get(slotKey) ?? 0;
|
|
18949
|
+
const tag = makePdfMergeSidecarPageTag(slotId, ordinal);
|
|
18950
|
+
ordinalBySlotKey.set(slotKey, ordinal + 1);
|
|
18951
|
+
writePdfMergePageTag(outputPages[outputIndex], tag);
|
|
18952
|
+
return { tag, slotId, sourceName: record.entry.name, sourceIndex: record.sourceIndex, rotation: record.rotation, outputIndex };
|
|
18953
|
+
});
|
|
18954
|
+
await attachPdfMergeSidecar(target, makePdfMergeSidecar(sidecarPages));
|
|
18378
18955
|
}
|
|
18379
18956
|
/**
|
|
18380
18957
|
* Merges every `ready` entry in the provided array order into a single PDF and returns it as a `Blob`. PDF entries contribute their full set of pages in order; image entries contribute one page sized to the image.
|
|
18381
18958
|
*
|
|
18382
|
-
*
|
|
18959
|
+
* Supplying {@link MergePdfMergeEntriesOptions.pages} replaces that default with an explicit page plan, so pages can be subsetted, reordered, and rotated. Supplying {@link MergePdfMergeEntriesOptions.sidecar} additionally embeds a manifest of which pages came from which slot.
|
|
18960
|
+
*
|
|
18961
|
+
* Special case: when the only `ready` entry is a single encrypted PDF, returns a passthrough `Blob` of its original bytes — `pdf-lib` cannot read encrypted PDFs even with `ignoreEncryption: true`, and downstream upload flows still need a usable blob. Neither the page plan nor the sidecar applies on that path, since the document cannot be opened.
|
|
18383
18962
|
*
|
|
18384
|
-
* Throws if no `ready` entries are provided,
|
|
18963
|
+
* Throws if no `ready` entries are provided, if multiple encrypted entries are passed in (the editor only routes here under focus mode where it has narrowed to one), or if the page plan leaves no pages to emit.
|
|
18385
18964
|
*
|
|
18386
18965
|
* @param entries - Ordered entries to merge.
|
|
18966
|
+
* @param options - Optional page plan and sidecar switch.
|
|
18387
18967
|
* @returns A Blob with `application/pdf` MIME type.
|
|
18388
18968
|
*/
|
|
18389
|
-
async function mergePdfMergeEntries(entries) {
|
|
18969
|
+
async function mergePdfMergeEntries(entries, options) {
|
|
18390
18970
|
const ready = entries.filter((entry) => entry.status === 'ready');
|
|
18391
18971
|
let result;
|
|
18392
18972
|
if (ready.length === 0) {
|
|
@@ -18401,13 +18981,14 @@ async function mergePdfMergeEntries(entries) {
|
|
|
18401
18981
|
}
|
|
18402
18982
|
else {
|
|
18403
18983
|
const target = await PDFDocument.create();
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18984
|
+
const loadSource = makePdfSourceDocumentLoader();
|
|
18985
|
+
const plan = options?.pages;
|
|
18986
|
+
const records = plan == null ? await appendAllEntryPages({ target, entries: ready, loadSource }) : await appendPlannedPages({ target, entries: ready, plan, loadSource });
|
|
18987
|
+
if (records.length === 0) {
|
|
18988
|
+
throw new Error('No pages to merge.');
|
|
18989
|
+
}
|
|
18990
|
+
if (options?.sidecar) {
|
|
18991
|
+
await writePdfMergeSidecarForRecords(target, records);
|
|
18411
18992
|
}
|
|
18412
18993
|
const bytes = await target.save();
|
|
18413
18994
|
result = new Blob([bytes], { type: PDF_MERGE_RESULT_MIME_TYPE });
|
|
@@ -18416,11 +18997,39 @@ async function mergePdfMergeEntries(entries) {
|
|
|
18416
18997
|
}
|
|
18417
18998
|
|
|
18418
18999
|
/**
|
|
18419
|
-
* Initial state used by {@link DbxPdfMergeEditorStore} — no entries.
|
|
19000
|
+
* Initial state used by {@link DbxPdfMergeEditorStore} — no entries and no page edits.
|
|
18420
19001
|
*/
|
|
18421
19002
|
const DBX_PDF_MERGE_EDITOR_INITIAL_STATE = {
|
|
18422
|
-
rawEntries: []
|
|
19003
|
+
rawEntries: [],
|
|
19004
|
+
pageOverrides: {},
|
|
19005
|
+
pageOrder: {}
|
|
18423
19006
|
};
|
|
19007
|
+
/**
|
|
19008
|
+
* Drops page overrides and stored orderings that belong to entries no longer in the list.
|
|
19009
|
+
*
|
|
19010
|
+
* The derived plan already ignores stale ids, so this is hygiene rather than correctness — it keeps the two records from growing without bound as the user adds and removes files.
|
|
19011
|
+
*
|
|
19012
|
+
* @param state - Current state.
|
|
19013
|
+
* @param liveEntryIds - Ids of the entries that remain.
|
|
19014
|
+
* @returns The pruned page-state slice.
|
|
19015
|
+
* @__NO_SIDE_EFFECTS__
|
|
19016
|
+
*/
|
|
19017
|
+
function prunePdfMergePageState(state, liveEntryIds) {
|
|
19018
|
+
const pageOverrides = {};
|
|
19019
|
+
const pageOrder = {};
|
|
19020
|
+
Object.entries(state.pageOverrides).forEach(([pageId, override]) => {
|
|
19021
|
+
if (liveEntryIds.has(entryIdForPdfMergePageId(pageId))) {
|
|
19022
|
+
pageOverrides[pageId] = override;
|
|
19023
|
+
}
|
|
19024
|
+
});
|
|
19025
|
+
Object.entries(state.pageOrder).forEach(([groupKey, pageIds]) => {
|
|
19026
|
+
const kept = pageIds.filter((pageId) => liveEntryIds.has(entryIdForPdfMergePageId(pageId)));
|
|
19027
|
+
if (kept.length > 0) {
|
|
19028
|
+
pageOrder[groupKey] = kept;
|
|
19029
|
+
}
|
|
19030
|
+
});
|
|
19031
|
+
return { pageOverrides, pageOrder };
|
|
19032
|
+
}
|
|
18424
19033
|
/**
|
|
18425
19034
|
* Component-scoped {@link ComponentStore} that owns the list of files staged for merging in the PDF merge editor. Each {@link PdfMergeEntry} carries its own validation promise from the moment it is built; {@link entries$} composes those promises into a live stream — emitting the entry first in `validating` state and then again as each promise resolves to `ready` or `error`. {@link mergeOutput$} emits the merged PDF blob once every entry has finished validating, at least one is `ready`, and the registered validator delegate (if any) reports `true`.
|
|
18426
19035
|
*/
|
|
@@ -18429,6 +19038,18 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18429
19038
|
_outputSizeLimit$ = new BehaviorSubject(undefined);
|
|
18430
19039
|
_imageCompression$ = new BehaviorSubject(undefined);
|
|
18431
19040
|
_encryptedHandling$ = new BehaviorSubject(undefined);
|
|
19041
|
+
_pageEditing$ = new BehaviorSubject(undefined);
|
|
19042
|
+
_sidecar$ = new BehaviorSubject(undefined);
|
|
19043
|
+
/**
|
|
19044
|
+
* Per-entry page metadata, parsed at most once per entry. Keyed by entry id; a resolved `null` means the entry could not be expanded. Only populated while page editing is enabled.
|
|
19045
|
+
*/
|
|
19046
|
+
_pageMetaCache = new Map();
|
|
19047
|
+
/**
|
|
19048
|
+
* Mount count per registered slot id. See {@link registerSlotId} for why this is a count rather than a set.
|
|
19049
|
+
*/
|
|
19050
|
+
_slotIdCounts = new Map();
|
|
19051
|
+
_registeredSlotIds$ = new BehaviorSubject([]);
|
|
19052
|
+
_importState$ = new BehaviorSubject(undefined);
|
|
18432
19053
|
constructor() {
|
|
18433
19054
|
super(DBX_PDF_MERGE_EDITOR_INITIAL_STATE);
|
|
18434
19055
|
}
|
|
@@ -18496,6 +19117,99 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18496
19117
|
*/
|
|
18497
19118
|
encryptedEntries$ = this.entries$.pipe(map((entries) => entries.filter((entry) => entry.encrypted && entry.status === 'ready')), shareReplay(1));
|
|
18498
19119
|
hasReadyEntries$ = this.displayEntries$.pipe(map((entries) => entries.some((entry) => entry.status === 'ready' && !entry.ignored)), distinctUntilChanged(), shareReplay(1));
|
|
19120
|
+
// MARK: Page editing
|
|
19121
|
+
/**
|
|
19122
|
+
* Raw {@link DbxPdfMergeEditorConfig.pageEditing} value pushed onto the store, before defaulting. Consumed by the editor as the middle tier of its resolution chain (own `[config]` input → store → {@link DBX_PDF_MERGE_EDITOR_CONFIG} token), so a store-level default does not shadow the token.
|
|
19123
|
+
*/
|
|
19124
|
+
pageEditingSetting$ = this._pageEditing$.asObservable();
|
|
19125
|
+
/**
|
|
19126
|
+
* Whether page editing is active, defaulted to {@link DEFAULT_DBX_PDF_MERGE_PAGE_EDITING}. Every page-related stream is inert while this is `false`.
|
|
19127
|
+
*/
|
|
19128
|
+
pageEditing$ = this.pageEditingSetting$.pipe(map((pageEditing) => pageEditing ?? DEFAULT_DBX_PDF_MERGE_PAGE_EDITING), distinctUntilChanged(), shareReplay(1));
|
|
19129
|
+
/**
|
|
19130
|
+
* Raw {@link DbxPdfMergeEditorConfig.sidecar} value pushed onto the store, before defaulting.
|
|
19131
|
+
*/
|
|
19132
|
+
sidecarSetting$ = this._sidecar$.asObservable();
|
|
19133
|
+
/**
|
|
19134
|
+
* Whether the merged output should carry an embedded manifest, defaulted to {@link DEFAULT_DBX_PDF_MERGE_SIDECAR}. Independent of {@link pageEditing$}.
|
|
19135
|
+
*/
|
|
19136
|
+
sidecar$ = this.sidecarSetting$.pipe(map((sidecar) => sidecar ?? DEFAULT_DBX_PDF_MERGE_SIDECAR), distinctUntilChanged(), shareReplay(1));
|
|
19137
|
+
/**
|
|
19138
|
+
* Hydrated page metadata keyed by entry id.
|
|
19139
|
+
*
|
|
19140
|
+
* Short-circuits to an empty record while page editing is off, so the default configuration never parses a source document — the upload path's cheap header scan stays the only inspection performed. Each entry is parsed at most once thanks to {@link _pageMetaCache}, and metadata for departed entries is pruned on each pass.
|
|
19141
|
+
*/
|
|
19142
|
+
pageMetas$ = combineLatest([this.entries$, this.pageEditing$]).pipe(switchMap(([entries, pageEditing]) => {
|
|
19143
|
+
const ready = pageEditing ? entries.filter((entry) => entry.status === 'ready') : [];
|
|
19144
|
+
let next$;
|
|
19145
|
+
if (ready.length === 0) {
|
|
19146
|
+
next$ = of({});
|
|
19147
|
+
}
|
|
19148
|
+
else {
|
|
19149
|
+
const readyIds = new Set(ready.map((entry) => entry.id));
|
|
19150
|
+
Array.from(this._pageMetaCache.keys()).forEach((entryId) => {
|
|
19151
|
+
if (!readyIds.has(entryId)) {
|
|
19152
|
+
this._pageMetaCache.delete(entryId);
|
|
19153
|
+
}
|
|
19154
|
+
});
|
|
19155
|
+
const loaded = ready.map(async (entry) => {
|
|
19156
|
+
let metas = this._pageMetaCache.get(entry.id);
|
|
19157
|
+
if (metas == null) {
|
|
19158
|
+
metas = readPdfMergeEntryPageMetas(entry);
|
|
19159
|
+
this._pageMetaCache.set(entry.id, metas);
|
|
19160
|
+
}
|
|
19161
|
+
return [entry.id, await metas];
|
|
19162
|
+
});
|
|
19163
|
+
next$ = from(Promise.all(loaded)).pipe(map((pairs) => Object.fromEntries(pairs)));
|
|
19164
|
+
}
|
|
19165
|
+
return next$;
|
|
19166
|
+
}), shareReplay(1));
|
|
19167
|
+
/**
|
|
19168
|
+
* The editor's ordered page plan, or `null` while page editing is disabled. Passing `null` through to the merge is what makes the default path emit every page of every entry exactly as it did before page editing existed.
|
|
19169
|
+
*/
|
|
19170
|
+
pages$ = combineLatest([this.displayEntries$, this.pageEditing$, this.pageMetas$, this.select((state) => state.pageOverrides), this.select((state) => state.pageOrder)]).pipe(map(([entries, pageEditing, pageMetas, pageOverrides, pageOrder]) => {
|
|
19171
|
+
let pages;
|
|
19172
|
+
if (pageEditing) {
|
|
19173
|
+
pages = buildPdfMergePagePlan({ entries, pageMetas, pageOverrides, pageOrder });
|
|
19174
|
+
}
|
|
19175
|
+
else {
|
|
19176
|
+
pages = null;
|
|
19177
|
+
}
|
|
19178
|
+
return pages;
|
|
19179
|
+
}), shareReplay(1));
|
|
19180
|
+
/**
|
|
19181
|
+
* The page plan split into the groups the UI renders, one per slot plus one for unslotted entries. Empty while page editing is disabled.
|
|
19182
|
+
*/
|
|
19183
|
+
pageGroups$ = this.pages$.pipe(map((pages) => {
|
|
19184
|
+
const grouped = makeValuesGroupMap(pages ?? [], (page) => page.groupKey);
|
|
19185
|
+
const groups = [];
|
|
19186
|
+
grouped.forEach((groupPages, groupKey) => {
|
|
19187
|
+
groups.push({ groupKey: groupKey, slotId: groupPages[0]?.slotId ?? null, pages: groupPages });
|
|
19188
|
+
});
|
|
19189
|
+
return groups;
|
|
19190
|
+
}), shareReplay(1));
|
|
19191
|
+
/**
|
|
19192
|
+
* Entries that are ready to merge but whose pages could not be listed — encrypted documents (which `pdf-lib` cannot open) and anything unparseable. Surfaced so the UI can explain why those rows are not expandable instead of letting them silently vanish from the page list. Always empty while page editing is disabled.
|
|
19193
|
+
*/
|
|
19194
|
+
unexpandableEntries$ = combineLatest([this.displayEntries$, this.pageEditing$, this.pageMetas$]).pipe(map(([entries, pageEditing, pageMetas]) => (pageEditing ? entries.filter((entry) => entry.status === 'ready' && !entry.ignored && pageMetas[entry.id] === null) : [])), shareReplay(1));
|
|
19195
|
+
/**
|
|
19196
|
+
* Number of pages that will reach the merged output — the plan minus anything marked for removal. Zero while page editing is disabled, where the count is not meaningful.
|
|
19197
|
+
*/
|
|
19198
|
+
mergeablePageCount$ = this.pages$.pipe(map((pages) => (pages ?? []).filter((page) => !page.removed).length), distinctUntilChanged(), shareReplay(1));
|
|
19199
|
+
/**
|
|
19200
|
+
* Whether at least one page survives the user's edits. Emits `true` while page editing is disabled so it never gates the default path.
|
|
19201
|
+
*/
|
|
19202
|
+
hasMergeablePages$ = this.pages$.pipe(map((pages) => pages == null || pages.some((page) => !page.removed)), distinctUntilChanged(), shareReplay(1));
|
|
19203
|
+
/**
|
|
19204
|
+
* Returns the pages belonging to one group, for a slot rendering its own pages inline.
|
|
19205
|
+
*
|
|
19206
|
+
* @param slotId - Slot identifier, or `null` for the unslotted group.
|
|
19207
|
+
* @returns Observable of that group's ordered pages.
|
|
19208
|
+
*/
|
|
19209
|
+
pagesForSlotId$(slotId) {
|
|
19210
|
+
const groupKey = pdfMergePageGroupKeyForSlotId(slotId);
|
|
19211
|
+
return this.pages$.pipe(map((pages) => (pages ?? []).filter((page) => page.groupKey === groupKey)), shareReplay(1));
|
|
19212
|
+
}
|
|
18499
19213
|
/**
|
|
18500
19214
|
* Emits `true` while any entry's validation promise has not yet resolved (i.e. one or more entries are still in `validating` status). Reads from {@link entries$} (not {@link displayEntries$}) so validation gating ignores the `ignored`/`error` projection done by encryption handling.
|
|
18501
19215
|
*/
|
|
@@ -18507,15 +19221,17 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18507
19221
|
/**
|
|
18508
19222
|
* Internal pre-validity merge stream produced without consulting {@link isValid$}. Drives both {@link outputSize$} and the eventual {@link currentMergeOutput$} so size-based gating can observe the would-be blob without creating a cycle. Consumes {@link displayEntries$} so the merge respects the active {@link DbxPdfMergeEncryptedHandling} (encrypted-focused entries pass through, ignored entries are dropped, `error` mode demotions are honored).
|
|
18509
19223
|
*/
|
|
18510
|
-
_candidateMergeOutput$ = combineLatest([this.displayEntries$, this.isValidating$, this.validatorValid$]).pipe(switchMap(([entries, isValidating, validatorValid]) => {
|
|
19224
|
+
_candidateMergeOutput$ = combineLatest([this.displayEntries$, this.isValidating$, this.validatorValid$, this.pages$, this.sidecar$]).pipe(switchMap(([entries, isValidating, validatorValid, pages, sidecar]) => {
|
|
18511
19225
|
const mergeable = entries.filter((entry) => !entry.ignored);
|
|
18512
19226
|
const hasReady = mergeable.some((entry) => entry.status === 'ready');
|
|
19227
|
+
// A `null` plan means page editing is off, in which case the merge takes its original every-page path.
|
|
19228
|
+
const hasPages = pages == null || pages.some((page) => !page.removed);
|
|
18513
19229
|
let next$;
|
|
18514
|
-
if (isValidating || !hasReady || !validatorValid) {
|
|
19230
|
+
if (isValidating || !hasReady || !validatorValid || !hasPages) {
|
|
18515
19231
|
next$ = of(undefined);
|
|
18516
19232
|
}
|
|
18517
19233
|
else {
|
|
18518
|
-
next$ = from(mergePdfMergeEntries(mergeable)).pipe(catchError(() => of(undefined)));
|
|
19234
|
+
next$ = from(mergePdfMergeEntries(mergeable, { pages, sidecar })).pipe(catchError(() => of(undefined)));
|
|
18519
19235
|
}
|
|
18520
19236
|
return next$;
|
|
18521
19237
|
}), shareReplay(1));
|
|
@@ -18537,9 +19253,11 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18537
19253
|
return valid;
|
|
18538
19254
|
}), distinctUntilChanged(), shareReplay(1));
|
|
18539
19255
|
/**
|
|
18540
|
-
* Emits `true` while
|
|
19256
|
+
* Emits `true` while the registered {@link DbxPdfMergeEditorValidator} delegate (if any), the optional output-size limit, and the page plan are all satisfied. {@link currentMergeOutput$} gates merge emissions on this stream.
|
|
19257
|
+
*
|
|
19258
|
+
* The page-plan term is `true` whenever page editing is disabled, so this stream is unchanged on the default path. With page editing on it blocks the merge once the user has marked every page for deletion.
|
|
18541
19259
|
*/
|
|
18542
|
-
isValid$ = combineLatest([this.validatorValid$, this.sizeLimitValid$]).pipe(map(([validatorValid, sizeLimitValid]) => validatorValid && sizeLimitValid), distinctUntilChanged(), shareReplay(1));
|
|
19260
|
+
isValid$ = combineLatest([this.validatorValid$, this.sizeLimitValid$, this.hasMergeablePages$]).pipe(map(([validatorValid, sizeLimitValid, hasMergeablePages]) => validatorValid && sizeLimitValid && hasMergeablePages), distinctUntilChanged(), shareReplay(1));
|
|
18543
19261
|
/**
|
|
18544
19262
|
* Emits the merged PDF blob whenever every entry has finished validating (see {@link isValidating$}), at least one is `ready`, and {@link isValid$} reports `true`. Emits `undefined` while validation is in flight, when the list is empty, when the delegate or size limit reports invalid, or when the most recent merge failed.
|
|
18545
19263
|
*/
|
|
@@ -18558,6 +19276,120 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18558
19276
|
entriesForSlotId$(slotId) {
|
|
18559
19277
|
return this.displayEntries$.pipe(map((entries) => entries.filter((entry) => entry.slotId === slotId)), shareReplay(1));
|
|
18560
19278
|
}
|
|
19279
|
+
// MARK: Slot registry
|
|
19280
|
+
/**
|
|
19281
|
+
* Slot ids currently mounted against this store, i.e. the sections this editor declares.
|
|
19282
|
+
*
|
|
19283
|
+
* This is what makes `<dbx-pdf-merge-import>` a zero-configuration drop-in: it derives the set of sections a re-imported file is allowed to name (and whether it renders at all) from the slots actually on screen, rather than from a hand-maintained list duplicating them.
|
|
19284
|
+
*
|
|
19285
|
+
* Emitted **sorted ascending**, which is deterministic but is NOT declaration order. Sorting is what lets the guard below collapse slot churn: a set cycling through `@if` (`['a','b']` → `['b']` → `['b','a']`) ends up equal to the array it started with, where registration order would emit a spuriously different array for an identical set.
|
|
19286
|
+
*
|
|
19287
|
+
* Registered by {@link DbxPdfMergeEditorFileUploadComponent} from its own lifecycle. Kept here rather than on the optional {@link DbxPdfMergeEditorFileUploadValidatorDirective} because it must be available whether or not a validator is present — and because a separately-provided registry could be provided at a different level than the store, letting slots register with one instance while a reader sees another.
|
|
19288
|
+
*/
|
|
19289
|
+
registeredSlotIds$ = this._registeredSlotIds$.pipe(distinctUntilChanged((a, b) => a.length === b.length && a.every((slotId, i) => slotId === b[i])), shareReplay(1));
|
|
19290
|
+
/**
|
|
19291
|
+
* Registers a slot id as mounted, adding it to {@link registeredSlotIds$}.
|
|
19292
|
+
*
|
|
19293
|
+
* Reference-counted rather than a set: the same `slotId` can legitimately be mounted twice at once — the PDF merge upload dialog hosts its slots against an ancestor store while inline slots may still exist under {@link DBX_PDF_MERGE_EDITOR_PRESERVE_ENTRIES_ON_SLOT_DESTROY} — and one unmount must not deregister a still-mounted id.
|
|
19294
|
+
*
|
|
19295
|
+
* @param slotId - Slot identifier being mounted.
|
|
19296
|
+
*/
|
|
19297
|
+
registerSlotId(slotId) {
|
|
19298
|
+
this._slotIdCounts.set(slotId, (this._slotIdCounts.get(slotId) ?? 0) + 1);
|
|
19299
|
+
this._emitRegisteredSlotIds();
|
|
19300
|
+
}
|
|
19301
|
+
/**
|
|
19302
|
+
* Deregisters one mount of a slot id, removing it from {@link registeredSlotIds$} once the last mount is gone. Unknown ids are ignored.
|
|
19303
|
+
*
|
|
19304
|
+
* Callers must pair this with their own {@link registerSlotId} exactly once. The store frequently outlives its slots (a dialog-hosted slot is destroyed on every close while the page-level store lives on), so a missed call leaves the editor permanently claiming a section that is not on screen.
|
|
19305
|
+
*
|
|
19306
|
+
* @param slotId - Slot identifier being unmounted.
|
|
19307
|
+
*/
|
|
19308
|
+
unregisterSlotId(slotId) {
|
|
19309
|
+
const count = this._slotIdCounts.get(slotId);
|
|
19310
|
+
if (count != null) {
|
|
19311
|
+
if (count > 1) {
|
|
19312
|
+
this._slotIdCounts.set(slotId, count - 1);
|
|
19313
|
+
}
|
|
19314
|
+
else {
|
|
19315
|
+
this._slotIdCounts.delete(slotId);
|
|
19316
|
+
}
|
|
19317
|
+
this._emitRegisteredSlotIds();
|
|
19318
|
+
}
|
|
19319
|
+
}
|
|
19320
|
+
_emitRegisteredSlotIds() {
|
|
19321
|
+
const next = Array.from(this._slotIdCounts.keys()).sort();
|
|
19322
|
+
const current = this._registeredSlotIds$.value;
|
|
19323
|
+
if (next.length !== current.length || next.some((slotId, i) => slotId !== current[i])) {
|
|
19324
|
+
this._registeredSlotIds$.next(next);
|
|
19325
|
+
}
|
|
19326
|
+
}
|
|
19327
|
+
// MARK: Import
|
|
19328
|
+
/**
|
|
19329
|
+
* Lifecycle of the most recent {@link importMergedPdf} call, or `undefined` before the first. Lets a consumer that triggered a programmatic import observe its outcome — the picker component is not the only entry point.
|
|
19330
|
+
*/
|
|
19331
|
+
importState$ = this._importState$.asObservable();
|
|
19332
|
+
/**
|
|
19333
|
+
* The most recent successful import, or `null` while none has succeeded.
|
|
19334
|
+
*/
|
|
19335
|
+
importResult$ = this.importState$.pipe(map((state) => (state?.status === 'imported' ? state.result : null)), distinctUntilChanged(), shareReplay(1));
|
|
19336
|
+
/**
|
|
19337
|
+
* Why the most recent import failed, or `null` when it did not.
|
|
19338
|
+
*/
|
|
19339
|
+
importError$ = this.importState$.pipe(map((state) => (state?.status === 'failed' ? state.error : null)), distinctUntilChanged(), shareReplay(1));
|
|
19340
|
+
/**
|
|
19341
|
+
* Emits `true` while an import is in flight.
|
|
19342
|
+
*/
|
|
19343
|
+
isImporting$ = this.importState$.pipe(map((state) => state?.status === 'importing'), distinctUntilChanged(), shareReplay(1));
|
|
19344
|
+
/**
|
|
19345
|
+
* Imports a previously-exported merged PDF, replacing every entry currently in the store with the per-slot documents recorded in its manifest.
|
|
19346
|
+
*
|
|
19347
|
+
* This is the programmatic equivalent of what {@link DbxPdfMergeImportComponent} does with a picked file, and that component delegates here — so a stored document loaded by an app and a file chosen by a user go through one implementation with one set of error semantics. Progress and outcome are published on {@link importState$} as well as returned.
|
|
19348
|
+
*
|
|
19349
|
+
* @param input - The source blob plus the optional slot check and sidecar-less fallback.
|
|
19350
|
+
* @returns The terminal state of this import.
|
|
19351
|
+
*/
|
|
19352
|
+
async importMergedPdf(input) {
|
|
19353
|
+
const { source, expectedSlotIds, allowWithoutSidecar, fileName } = input;
|
|
19354
|
+
const config = { allowWithoutSidecar, fileName };
|
|
19355
|
+
this._importState$.next({ status: 'importing' });
|
|
19356
|
+
const outcome = await buildPdfMergeEntriesFromSidecar(source, config);
|
|
19357
|
+
let state;
|
|
19358
|
+
if ('error' in outcome) {
|
|
19359
|
+
state = { status: 'failed', error: outcome.error };
|
|
19360
|
+
}
|
|
19361
|
+
else {
|
|
19362
|
+
// An unsectioned document counts as unexpected too: in a slots-only editor it would render
|
|
19363
|
+
// nowhere, so rejecting it beats importing pages the user can never see again.
|
|
19364
|
+
const unexpected = expectedSlotIds == null ? [] : outcome.slotIds.filter((slotId) => slotId == null || !expectedSlotIds.includes(slotId));
|
|
19365
|
+
if (unexpected.length > 0) {
|
|
19366
|
+
state = { status: 'failed', error: 'unexpected_slots', unexpectedSlotIds: unexpected };
|
|
19367
|
+
}
|
|
19368
|
+
else {
|
|
19369
|
+
const missingSlotIds = expectedSlotIds == null ? [] : expectedSlotIds.filter((slotId) => !outcome.slotIds.includes(slotId));
|
|
19370
|
+
this.replaceEntries(outcome.entries);
|
|
19371
|
+
state = { status: 'imported', result: { ...outcome, missingSlotIds } };
|
|
19372
|
+
}
|
|
19373
|
+
}
|
|
19374
|
+
this._importState$.next(state);
|
|
19375
|
+
return state;
|
|
19376
|
+
}
|
|
19377
|
+
/**
|
|
19378
|
+
* Adds a single document to a slot, without any sidecar involvement — the programmatic equivalent of a user dropping one file onto a slot uploader. Existing entries are left alone.
|
|
19379
|
+
*
|
|
19380
|
+
* Unlike {@link importMergedPdf} this neither reads nor requires a manifest; it is the plain "put this file in that slot" primitive, for e.g. an admin appending one document to an existing set. The entry can be added before its slot mounts — {@link entriesForSlotId$} picks it up when the slot renders.
|
|
19381
|
+
*
|
|
19382
|
+
* @param input - The blob or file, its target slot, and an optional name for a bare blob.
|
|
19383
|
+
* @returns The entry that was added, or `null` when the file is not a supported PDF/PNG/JPEG (in which case nothing was added).
|
|
19384
|
+
*/
|
|
19385
|
+
async addFileToSlot(input) {
|
|
19386
|
+
const { file, slotId, fileName, imageCompression } = input;
|
|
19387
|
+
const entry = await buildPdfMergeEntry(asPdfMergeFile(file, fileName), { slotId, imageCompression: imageCompression ?? this._imageCompression$.value });
|
|
19388
|
+
if (entry != null) {
|
|
19389
|
+
this.addFiles({ entries: [entry] });
|
|
19390
|
+
}
|
|
19391
|
+
return entry;
|
|
19392
|
+
}
|
|
18561
19393
|
// MARK: Validator
|
|
18562
19394
|
/**
|
|
18563
19395
|
* Registers a {@link DbxPdfMergeEditorValidator} delegate that gates merge emissions. Only one delegate is active at a time — calling this replaces any previously registered delegate.
|
|
@@ -18597,6 +19429,22 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18597
19429
|
setEncryptedHandling(handling) {
|
|
18598
19430
|
this._encryptedHandling$.next(handling ?? undefined);
|
|
18599
19431
|
}
|
|
19432
|
+
/**
|
|
19433
|
+
* Enables or disables page editing, exposed via {@link pageEditing$}. Turning it on expands each ready entry into its individual pages; turning it off returns the editor to its file-granular behavior while preserving any page edits in state, so toggling back on restores them.
|
|
19434
|
+
*
|
|
19435
|
+
* @param pageEditing - Whether page editing is active, or a falsy value to clear and fall back to {@link DEFAULT_DBX_PDF_MERGE_PAGE_EDITING}.
|
|
19436
|
+
*/
|
|
19437
|
+
setPageEditing(pageEditing) {
|
|
19438
|
+
this._pageEditing$.next(pageEditing ?? undefined);
|
|
19439
|
+
}
|
|
19440
|
+
/**
|
|
19441
|
+
* Enables or disables the embedded manifest, exposed via {@link sidecar$}.
|
|
19442
|
+
*
|
|
19443
|
+
* @param sidecar - Whether to embed the manifest, or a falsy value to clear and fall back to {@link DEFAULT_DBX_PDF_MERGE_SIDECAR}.
|
|
19444
|
+
*/
|
|
19445
|
+
setSidecar(sidecar) {
|
|
19446
|
+
this._sidecar$.next(sidecar ?? undefined);
|
|
19447
|
+
}
|
|
18600
19448
|
// MARK: Updaters
|
|
18601
19449
|
/**
|
|
18602
19450
|
* Appends entries (already constructed) or builds them from raw files and appends them to state. Each entry's validation promise starts when the entry is built; {@link entries$} reflects each result as it resolves. When `input` is an object with `files` and `slotId`, the resulting entries are tagged with that slot id. When `input` is `{ entries }`, the entries are appended as-is — use this shape for entries that went through async client-side compression upstream.
|
|
@@ -18619,11 +19467,17 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18619
19467
|
}
|
|
18620
19468
|
return newEntries.length > 0 ? { ...state, rawEntries: [...state.rawEntries, ...newEntries] } : state;
|
|
18621
19469
|
});
|
|
18622
|
-
removeEntry = this.updater((state, id) =>
|
|
19470
|
+
removeEntry = this.updater((state, id) => {
|
|
19471
|
+
const rawEntries = state.rawEntries.filter((entry) => entry.id !== id);
|
|
19472
|
+
return { ...state, rawEntries, ...prunePdfMergePageState(state, new Set(rawEntries.map((entry) => entry.id))) };
|
|
19473
|
+
});
|
|
18623
19474
|
/**
|
|
18624
19475
|
* Removes every entry whose `slotId` matches the given id. Used by {@link DbxPdfMergeEditorFileUploadComponent} on destroy so a slot's entries leave with it.
|
|
18625
19476
|
*/
|
|
18626
|
-
removeEntriesBySlotId = this.updater((state, slotId) =>
|
|
19477
|
+
removeEntriesBySlotId = this.updater((state, slotId) => {
|
|
19478
|
+
const rawEntries = state.rawEntries.filter((entry) => entry.slotId !== slotId);
|
|
19479
|
+
return { ...state, rawEntries, ...prunePdfMergePageState(state, new Set(rawEntries.map((entry) => entry.id))) };
|
|
19480
|
+
});
|
|
18627
19481
|
moveEntry = this.updater((state, move) => {
|
|
18628
19482
|
let nextState;
|
|
18629
19483
|
if (move.previousIndex === move.currentIndex) {
|
|
@@ -18663,7 +19517,49 @@ class DbxPdfMergeEditorStore extends ComponentStore {
|
|
|
18663
19517
|
}
|
|
18664
19518
|
return nextState;
|
|
18665
19519
|
});
|
|
18666
|
-
clearAll = this.updater((state) => ({ ...state, rawEntries: [] }));
|
|
19520
|
+
clearAll = this.updater((state) => ({ ...state, rawEntries: [], pageOverrides: {}, pageOrder: {} }));
|
|
19521
|
+
/**
|
|
19522
|
+
* Replaces the entire entry list, discarding any page edits.
|
|
19523
|
+
*
|
|
19524
|
+
* Used by the sidecar import path, which reconstructs a fresh set of slot-tagged entries from a previously-exported file. Done as a single state change so subscribers never observe an intermediate empty list.
|
|
19525
|
+
*/
|
|
19526
|
+
replaceEntries = this.updater((state, entries) => ({ ...state, rawEntries: [...entries], pageOverrides: {}, pageOrder: {} }));
|
|
19527
|
+
// MARK: Page updaters
|
|
19528
|
+
/**
|
|
19529
|
+
* Reorders a page inside its own group. Groups never exchange pages — the page list renders one CDK drop list per group, so a cross-group drag is impossible to express.
|
|
19530
|
+
*
|
|
19531
|
+
* The move rewrites the id array the caller rendered rather than recomputing it from state, keeping the stored order aligned with what the user saw.
|
|
19532
|
+
*/
|
|
19533
|
+
movePageWithinGroup = this.updater((state, move) => {
|
|
19534
|
+
let nextState;
|
|
19535
|
+
if (move.previousIndex === move.currentIndex || move.previousIndex < 0 || move.currentIndex < 0 || move.previousIndex >= move.pageIds.length || move.currentIndex >= move.pageIds.length) {
|
|
19536
|
+
nextState = state;
|
|
19537
|
+
}
|
|
19538
|
+
else {
|
|
19539
|
+
const pageIds = [...move.pageIds];
|
|
19540
|
+
moveItemInArray(pageIds, move.previousIndex, move.currentIndex);
|
|
19541
|
+
nextState = { ...state, pageOrder: { ...state.pageOrder, [move.groupKey]: pageIds } };
|
|
19542
|
+
}
|
|
19543
|
+
return nextState;
|
|
19544
|
+
});
|
|
19545
|
+
/**
|
|
19546
|
+
* Sets a page's rotation. The value is composed onto the page's own source rotation at merge time, so this is the rotation the user applied rather than the page's absolute orientation.
|
|
19547
|
+
*/
|
|
19548
|
+
setPageRotation = this.updater((state, change) => {
|
|
19549
|
+
const current = state.pageOverrides[change.pageId];
|
|
19550
|
+
return { ...state, pageOverrides: { ...state.pageOverrides, [change.pageId]: { rotation: change.rotation, removed: current?.removed ?? false } } };
|
|
19551
|
+
});
|
|
19552
|
+
/**
|
|
19553
|
+
* Marks a page for deletion or restores it. Marked pages stay in the list so the choice can be undone, but are excluded from the merged output.
|
|
19554
|
+
*/
|
|
19555
|
+
setPageRemoved = this.updater((state, change) => {
|
|
19556
|
+
const current = state.pageOverrides[change.pageId];
|
|
19557
|
+
return { ...state, pageOverrides: { ...state.pageOverrides, [change.pageId]: { rotation: current?.rotation ?? 0, removed: change.removed } } };
|
|
19558
|
+
});
|
|
19559
|
+
/**
|
|
19560
|
+
* Discards every page edit, returning the plan to natural order with no rotations or deletions. Leaves the entries themselves untouched.
|
|
19561
|
+
*/
|
|
19562
|
+
clearPageEdits = this.updater((state) => ({ ...state, pageOverrides: {}, pageOrder: {} }));
|
|
18667
19563
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
18668
19564
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorStore });
|
|
18669
19565
|
}
|
|
@@ -18688,6 +19584,19 @@ class DbxPdfMergeEditorStoreDirective {
|
|
|
18688
19584
|
_injectedConfig = inject(DBX_PDF_MERGE_EDITOR_CONFIG, { optional: true });
|
|
18689
19585
|
config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : /* istanbul ignore next */ []));
|
|
18690
19586
|
outputSizeLimit = input(...(ngDevMode ? [undefined, { debugName: "outputSizeLimit" }] : /* istanbul ignore next */ []));
|
|
19587
|
+
/**
|
|
19588
|
+
* A previously-exported merged PDF to load into the store, for an app that fetches a stored document itself and wants the editor to open already populated — no picker involved.
|
|
19589
|
+
*
|
|
19590
|
+
* Imported once per distinct blob instance: re-emitting the same object does nothing, so a template re-render or an unrelated input change cannot re-import. Emitting a *different* blob imports again and replaces the entries, even if the bytes are identical.
|
|
19591
|
+
*
|
|
19592
|
+
* Watch {@link DbxPdfMergeEditorStore.importState$} for the outcome — a failure surfaces there rather than throwing.
|
|
19593
|
+
*/
|
|
19594
|
+
source = input(...(ngDevMode ? [undefined, { debugName: "source" }] : /* istanbul ignore next */ []));
|
|
19595
|
+
sourceConfig = input(...(ngDevMode ? [undefined, { debugName: "sourceConfig" }] : /* istanbul ignore next */ []));
|
|
19596
|
+
/**
|
|
19597
|
+
* The blob instance most recently handed to the store, so the same one is never imported twice.
|
|
19598
|
+
*/
|
|
19599
|
+
_importedSource;
|
|
18691
19600
|
effectiveErrorBytesSignal = computed(() => {
|
|
18692
19601
|
const direct = this.outputSizeLimit();
|
|
18693
19602
|
if (direct != null) {
|
|
@@ -18719,9 +19628,30 @@ class DbxPdfMergeEditorStoreDirective {
|
|
|
18719
19628
|
effect(() => {
|
|
18720
19629
|
this.store.setEncryptedHandling(this.config()?.encryptedHandling);
|
|
18721
19630
|
});
|
|
19631
|
+
// Same channel for pageEditing, so a dialog-hosted editor opens straight into page
|
|
19632
|
+
// editing mode when the host directive asked for it.
|
|
19633
|
+
effect(() => {
|
|
19634
|
+
this.store.setPageEditing(this.config()?.pageEditing);
|
|
19635
|
+
});
|
|
19636
|
+
// Same channel for the embedded manifest. Gated separately from pageEditing — a plain
|
|
19637
|
+
// slot merge benefits from recording which pages came from which slot too.
|
|
19638
|
+
effect(() => {
|
|
19639
|
+
this.store.setSidecar(this.config()?.sidecar);
|
|
19640
|
+
});
|
|
19641
|
+
// Loading a source is not idempotent the way the setters above are — it REPLACES the store's
|
|
19642
|
+
// entries — so this tracks only `source` and guards on instance identity. `sourceConfig` is
|
|
19643
|
+
// read untracked so changing it never re-imports; it is read at the moment a new blob arrives.
|
|
19644
|
+
effect(() => {
|
|
19645
|
+
const source = this.source();
|
|
19646
|
+
if (source != null && source !== this._importedSource) {
|
|
19647
|
+
this._importedSource = source;
|
|
19648
|
+
const sourceConfig = untracked(() => this.sourceConfig());
|
|
19649
|
+
void this.store.importMergedPdf({ source, expectedSlotIds: sourceConfig?.expectedSlotIds, allowWithoutSidecar: sourceConfig?.allowWithoutSidecar, fileName: sourceConfig?.fileName });
|
|
19650
|
+
}
|
|
19651
|
+
});
|
|
18722
19652
|
}
|
|
18723
19653
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorStoreDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
18724
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.11", type: DbxPdfMergeEditorStoreDirective, isStandalone: true, selector: "[dbxPdfMergeEditorStore]", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, outputSizeLimit: { classPropertyName: "outputSizeLimit", publicName: "outputSizeLimit", isSignal: true, isRequired: false, transformFunction: null } }, providers: [DbxPdfMergeEditorStore], exportAs: ["dbxPdfMergeEditorStore"], ngImport: i0 });
|
|
19654
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.11", type: DbxPdfMergeEditorStoreDirective, isStandalone: true, selector: "[dbxPdfMergeEditorStore]", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, outputSizeLimit: { classPropertyName: "outputSizeLimit", publicName: "outputSizeLimit", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, sourceConfig: { classPropertyName: "sourceConfig", publicName: "sourceConfig", isSignal: true, isRequired: false, transformFunction: null } }, providers: [DbxPdfMergeEditorStore], exportAs: ["dbxPdfMergeEditorStore"], ngImport: i0 });
|
|
18725
19655
|
}
|
|
18726
19656
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorStoreDirective, decorators: [{
|
|
18727
19657
|
type: Directive,
|
|
@@ -18731,10 +19661,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
18731
19661
|
providers: [DbxPdfMergeEditorStore],
|
|
18732
19662
|
exportAs: 'dbxPdfMergeEditorStore'
|
|
18733
19663
|
}]
|
|
18734
|
-
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], outputSizeLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "outputSizeLimit", required: false }] }] } });
|
|
19664
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], outputSizeLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "outputSizeLimit", required: false }] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], sourceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceConfig", required: false }] }] } });
|
|
18735
19665
|
|
|
18736
|
-
const PDF_ICON = 'picture_as_pdf';
|
|
18737
|
-
const IMAGE_ICON = 'image';
|
|
19666
|
+
const PDF_ICON$1 = 'picture_as_pdf';
|
|
19667
|
+
const IMAGE_ICON$1 = 'image';
|
|
18738
19668
|
const ERROR_ICON = 'error';
|
|
18739
19669
|
/**
|
|
18740
19670
|
* Single row inside the {@link DbxPdfMergeListComponent}: shows the file's icon, name, formatted size, status, drag handle, and a remove button. The component's template is itself a `cdkDrag` element so each row can be reordered inside the parent's `cdkDropList`.
|
|
@@ -18751,10 +19681,10 @@ class DbxPdfMergeEntryComponent {
|
|
|
18751
19681
|
icon = ERROR_ICON;
|
|
18752
19682
|
}
|
|
18753
19683
|
else if (entry.kind === 'pdf') {
|
|
18754
|
-
icon = PDF_ICON;
|
|
19684
|
+
icon = PDF_ICON$1;
|
|
18755
19685
|
}
|
|
18756
19686
|
else {
|
|
18757
|
-
icon = IMAGE_ICON;
|
|
19687
|
+
icon = IMAGE_ICON$1;
|
|
18758
19688
|
}
|
|
18759
19689
|
return icon;
|
|
18760
19690
|
}, ...(ngDevMode ? [{ debugName: "iconSignal" }] : /* istanbul ignore next */ []));
|
|
@@ -18940,6 +19870,275 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
18940
19870
|
}]
|
|
18941
19871
|
}] });
|
|
18942
19872
|
|
|
19873
|
+
const PDF_ICON = 'picture_as_pdf';
|
|
19874
|
+
const IMAGE_ICON = 'image';
|
|
19875
|
+
/**
|
|
19876
|
+
* Degrees added by one press of the rotate control.
|
|
19877
|
+
*/
|
|
19878
|
+
const ROTATION_STEP_DEGREES = 90;
|
|
19879
|
+
/**
|
|
19880
|
+
* PDF points per inch. PDF page dimensions are expressed in points; inches read better on a scanned document.
|
|
19881
|
+
*/
|
|
19882
|
+
const POINTS_PER_INCH = 72;
|
|
19883
|
+
const ROTATION_VALUES = [0, 90, 180, 270];
|
|
19884
|
+
/**
|
|
19885
|
+
* Single page row inside a {@link DbxPdfMergePageListComponent} group: shows which source file and page it came from, its dimensions and rotation, and controls to rotate it, mark it for removal, and drag it to a new position.
|
|
19886
|
+
*
|
|
19887
|
+
* The template's root element is itself a `cdkDrag`, matching {@link DbxPdfMergeEntryComponent}, so rows reorder inside their group's drop list.
|
|
19888
|
+
*/
|
|
19889
|
+
class DbxPdfMergePageComponent {
|
|
19890
|
+
store = inject(DbxPdfMergeEditorStore);
|
|
19891
|
+
page = input.required(...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
19892
|
+
removedSignal = computed(() => this.page().removed, ...(ngDevMode ? [{ debugName: "removedSignal" }] : /* istanbul ignore next */ []));
|
|
19893
|
+
iconSignal = computed(() => (this.page().kind === 'pdf' ? PDF_ICON : IMAGE_ICON), ...(ngDevMode ? [{ debugName: "iconSignal" }] : /* istanbul ignore next */ []));
|
|
19894
|
+
removeLabelSignal = computed(() => (this.removedSignal() ? 'Restore page' : 'Mark page for removal'), ...(ngDevMode ? [{ debugName: "removeLabelSignal" }] : /* istanbul ignore next */ []));
|
|
19895
|
+
positionLabelSignal = computed(() => {
|
|
19896
|
+
const page = this.page();
|
|
19897
|
+
let label;
|
|
19898
|
+
if (page.kind === 'image') {
|
|
19899
|
+
label = 'Image';
|
|
19900
|
+
}
|
|
19901
|
+
else {
|
|
19902
|
+
label = `Page ${page.sourceIndex + 1} of ${page.sourcePageCount}`;
|
|
19903
|
+
}
|
|
19904
|
+
return label;
|
|
19905
|
+
}, ...(ngDevMode ? [{ debugName: "positionLabelSignal" }] : /* istanbul ignore next */ []));
|
|
19906
|
+
dimensionsLabelSignal = computed(() => {
|
|
19907
|
+
const { kind, meta } = this.page();
|
|
19908
|
+
let label;
|
|
19909
|
+
if (meta.width <= 0 || meta.height <= 0) {
|
|
19910
|
+
label = null;
|
|
19911
|
+
}
|
|
19912
|
+
else if (kind === 'image') {
|
|
19913
|
+
label = `${Math.round(meta.width)} × ${Math.round(meta.height)} px`;
|
|
19914
|
+
}
|
|
19915
|
+
else {
|
|
19916
|
+
label = `${(meta.width / POINTS_PER_INCH).toFixed(1)} × ${(meta.height / POINTS_PER_INCH).toFixed(1)} in`;
|
|
19917
|
+
}
|
|
19918
|
+
return label;
|
|
19919
|
+
}, ...(ngDevMode ? [{ debugName: "dimensionsLabelSignal" }] : /* istanbul ignore next */ []));
|
|
19920
|
+
rotationLabelSignal = computed(() => {
|
|
19921
|
+
const rotation = this.page().rotation;
|
|
19922
|
+
return rotation === 0 ? null : `${rotation}°`;
|
|
19923
|
+
}, ...(ngDevMode ? [{ debugName: "rotationLabelSignal" }] : /* istanbul ignore next */ []));
|
|
19924
|
+
onRotate() {
|
|
19925
|
+
const page = this.page();
|
|
19926
|
+
const next = ROTATION_VALUES[((page.rotation + ROTATION_STEP_DEGREES) / ROTATION_STEP_DEGREES) % ROTATION_VALUES.length];
|
|
19927
|
+
this.store.setPageRotation({ pageId: page.id, rotation: next });
|
|
19928
|
+
}
|
|
19929
|
+
onToggleRemoved() {
|
|
19930
|
+
const page = this.page();
|
|
19931
|
+
this.store.setPageRemoved({ pageId: page.id, removed: !page.removed });
|
|
19932
|
+
}
|
|
19933
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergePageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19934
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: DbxPdfMergePageComponent, isStandalone: true, selector: "dbx-pdf-merge-page", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.dbx-pdf-merge-page--removed": "removedSignal()" }, classAttribute: "dbx-pdf-merge-page d-block" }, ngImport: i0, template: `
|
|
19935
|
+
<div class="dbx-pdf-merge-page-row" cdkDrag cdkDragLockAxis="y">
|
|
19936
|
+
<ng-template cdkDragPlaceholder>
|
|
19937
|
+
<div class="dbx-pdf-merge-page-placeholder"></div>
|
|
19938
|
+
</ng-template>
|
|
19939
|
+
<button mat-icon-button type="button" cdkDragHandle class="dbx-pdf-merge-page-handle" aria-label="Drag to reorder page">
|
|
19940
|
+
<mat-icon>drag_indicator</mat-icon>
|
|
19941
|
+
</button>
|
|
19942
|
+
<mat-icon class="dbx-pdf-merge-page-icon">{{ iconSignal() }}</mat-icon>
|
|
19943
|
+
<div class="dbx-pdf-merge-page-info dbx-flex-fill-0">
|
|
19944
|
+
<div class="dbx-pdf-merge-page-name dbx-text-truncate" [title]="page().sourceName">{{ page().sourceName }}</div>
|
|
19945
|
+
<div class="dbx-pdf-merge-page-meta dbx-hint dbx-small">
|
|
19946
|
+
<span class="dbx-pdf-merge-page-position">{{ positionLabelSignal() }}</span>
|
|
19947
|
+
@if (dimensionsLabelSignal(); as dimensions) {
|
|
19948
|
+
<span class="dbx-pdf-merge-page-dimensions">{{ dimensions }}</span>
|
|
19949
|
+
}
|
|
19950
|
+
@if (rotationLabelSignal(); as rotation) {
|
|
19951
|
+
<dbx-chip class="dbx-pdf-merge-page-rotation-chip" [small]="true">
|
|
19952
|
+
<mat-icon class="dbx-pdf-merge-page-rotation-icon">rotate_right</mat-icon>
|
|
19953
|
+
<span>{{ rotation }}</span>
|
|
19954
|
+
</dbx-chip>
|
|
19955
|
+
}
|
|
19956
|
+
@if (removedSignal()) {
|
|
19957
|
+
<span class="dbx-pdf-merge-page-removed-label dbx-warn">Will be removed</span>
|
|
19958
|
+
}
|
|
19959
|
+
</div>
|
|
19960
|
+
</div>
|
|
19961
|
+
<button mat-icon-button type="button" class="dbx-pdf-merge-page-rotate" [disabled]="removedSignal()" (click)="onRotate()" aria-label="Rotate page 90 degrees">
|
|
19962
|
+
<mat-icon>rotate_right</mat-icon>
|
|
19963
|
+
</button>
|
|
19964
|
+
<button mat-icon-button type="button" class="dbx-pdf-merge-page-remove" (click)="onToggleRemoved()" [attr.aria-label]="removeLabelSignal()">
|
|
19965
|
+
<mat-icon>{{ removedSignal() ? 'undo' : 'close' }}</mat-icon>
|
|
19966
|
+
</button>
|
|
19967
|
+
</div>
|
|
19968
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: DbxChipDirective, selector: "dbx-chip", inputs: ["small", "block", "color", "display", "tone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19969
|
+
}
|
|
19970
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergePageComponent, decorators: [{
|
|
19971
|
+
type: Component,
|
|
19972
|
+
args: [{
|
|
19973
|
+
selector: 'dbx-pdf-merge-page',
|
|
19974
|
+
template: `
|
|
19975
|
+
<div class="dbx-pdf-merge-page-row" cdkDrag cdkDragLockAxis="y">
|
|
19976
|
+
<ng-template cdkDragPlaceholder>
|
|
19977
|
+
<div class="dbx-pdf-merge-page-placeholder"></div>
|
|
19978
|
+
</ng-template>
|
|
19979
|
+
<button mat-icon-button type="button" cdkDragHandle class="dbx-pdf-merge-page-handle" aria-label="Drag to reorder page">
|
|
19980
|
+
<mat-icon>drag_indicator</mat-icon>
|
|
19981
|
+
</button>
|
|
19982
|
+
<mat-icon class="dbx-pdf-merge-page-icon">{{ iconSignal() }}</mat-icon>
|
|
19983
|
+
<div class="dbx-pdf-merge-page-info dbx-flex-fill-0">
|
|
19984
|
+
<div class="dbx-pdf-merge-page-name dbx-text-truncate" [title]="page().sourceName">{{ page().sourceName }}</div>
|
|
19985
|
+
<div class="dbx-pdf-merge-page-meta dbx-hint dbx-small">
|
|
19986
|
+
<span class="dbx-pdf-merge-page-position">{{ positionLabelSignal() }}</span>
|
|
19987
|
+
@if (dimensionsLabelSignal(); as dimensions) {
|
|
19988
|
+
<span class="dbx-pdf-merge-page-dimensions">{{ dimensions }}</span>
|
|
19989
|
+
}
|
|
19990
|
+
@if (rotationLabelSignal(); as rotation) {
|
|
19991
|
+
<dbx-chip class="dbx-pdf-merge-page-rotation-chip" [small]="true">
|
|
19992
|
+
<mat-icon class="dbx-pdf-merge-page-rotation-icon">rotate_right</mat-icon>
|
|
19993
|
+
<span>{{ rotation }}</span>
|
|
19994
|
+
</dbx-chip>
|
|
19995
|
+
}
|
|
19996
|
+
@if (removedSignal()) {
|
|
19997
|
+
<span class="dbx-pdf-merge-page-removed-label dbx-warn">Will be removed</span>
|
|
19998
|
+
}
|
|
19999
|
+
</div>
|
|
20000
|
+
</div>
|
|
20001
|
+
<button mat-icon-button type="button" class="dbx-pdf-merge-page-rotate" [disabled]="removedSignal()" (click)="onRotate()" aria-label="Rotate page 90 degrees">
|
|
20002
|
+
<mat-icon>rotate_right</mat-icon>
|
|
20003
|
+
</button>
|
|
20004
|
+
<button mat-icon-button type="button" class="dbx-pdf-merge-page-remove" (click)="onToggleRemoved()" [attr.aria-label]="removeLabelSignal()">
|
|
20005
|
+
<mat-icon>{{ removedSignal() ? 'undo' : 'close' }}</mat-icon>
|
|
20006
|
+
</button>
|
|
20007
|
+
</div>
|
|
20008
|
+
`,
|
|
20009
|
+
host: {
|
|
20010
|
+
class: 'dbx-pdf-merge-page d-block',
|
|
20011
|
+
'[class.dbx-pdf-merge-page--removed]': 'removedSignal()'
|
|
20012
|
+
},
|
|
20013
|
+
imports: [CdkDrag, CdkDragHandle, CdkDragPlaceholder, MatIconModule, MatButtonModule, DbxChipDirective],
|
|
20014
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
20015
|
+
standalone: true
|
|
20016
|
+
}]
|
|
20017
|
+
}], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: true }] }] } });
|
|
20018
|
+
|
|
20019
|
+
/**
|
|
20020
|
+
* Message shown for an entry that is not encrypted but still could not be parsed into pages.
|
|
20021
|
+
*/
|
|
20022
|
+
const UNREADABLE_PAGES_MESSAGE = 'Pages could not be read.';
|
|
20023
|
+
/**
|
|
20024
|
+
* Renders the editor's page plan while page editing is enabled, one CDK drop list per group.
|
|
20025
|
+
*
|
|
20026
|
+
* Separate, unconnected drop lists are what confine reordering to a single slot: CDK will not let a drag cross into a list it is not connected to, so the rule needs no guard code. Bind {@link slotId} to render only that slot's group — used by {@link DbxPdfMergeEditorFileUploadComponent} to show its own pages inline. Leave it unbound to render every group.
|
|
20027
|
+
*
|
|
20028
|
+
* Entries that could not be expanded into pages (encrypted, or unparseable) are listed separately below the groups rather than silently omitted.
|
|
20029
|
+
*/
|
|
20030
|
+
class DbxPdfMergePageListComponent {
|
|
20031
|
+
store = inject(DbxPdfMergeEditorStore);
|
|
20032
|
+
/**
|
|
20033
|
+
* Renders only this slot's group when bound. Leave unbound to render every group.
|
|
20034
|
+
*/
|
|
20035
|
+
slotId = input(...(ngDevMode ? [undefined, { debugName: "slotId" }] : /* istanbul ignore next */ []));
|
|
20036
|
+
/**
|
|
20037
|
+
* Whether to show each group's slot name above its pages. Defaults to `true` when rendering every group and `false` when scoped to one slot, where the slot already has its own heading.
|
|
20038
|
+
*/
|
|
20039
|
+
showGroupLabels = input(...(ngDevMode ? [undefined, { debugName: "showGroupLabels" }] : /* istanbul ignore next */ []));
|
|
20040
|
+
_slotId$ = toObservable(this.slotId);
|
|
20041
|
+
groups$ = this._slotId$.pipe(switchMap((slotId) => {
|
|
20042
|
+
let groups$;
|
|
20043
|
+
if (slotId == null) {
|
|
20044
|
+
groups$ = this.store.pageGroups$;
|
|
20045
|
+
}
|
|
20046
|
+
else {
|
|
20047
|
+
const groupKey = pdfMergePageGroupKeyForSlotId(slotId);
|
|
20048
|
+
groups$ = this.store.pagesForSlotId$(slotId).pipe(map((pages) => (pages.length === 0 ? [] : [{ groupKey, slotId, pages }])));
|
|
20049
|
+
}
|
|
20050
|
+
return groups$;
|
|
20051
|
+
}), shareReplay(1));
|
|
20052
|
+
unexpandable$ = combineLatest([this.store.unexpandableEntries$, this._slotId$]).pipe(map(([entries, slotId]) => (slotId == null ? entries : entries.filter((entry) => entry.slotId === slotId))), shareReplay(1));
|
|
20053
|
+
groupsSignal = toSignal(this.groups$, { initialValue: [] });
|
|
20054
|
+
unexpandableSignal = toSignal(this.unexpandable$, { initialValue: [] });
|
|
20055
|
+
showGroupLabelsSignal = computed(() => {
|
|
20056
|
+
const slotId = this.slotId();
|
|
20057
|
+
return this.showGroupLabels() ?? slotId == null;
|
|
20058
|
+
}, ...(ngDevMode ? [{ debugName: "showGroupLabelsSignal" }] : /* istanbul ignore next */ []));
|
|
20059
|
+
labelForGroup(group) {
|
|
20060
|
+
return this.showGroupLabelsSignal() ? group.slotId : null;
|
|
20061
|
+
}
|
|
20062
|
+
messageForUnexpandable(entry) {
|
|
20063
|
+
return entry.encrypted ? DBX_PDF_MERGE_ENCRYPTED_NOT_EDITABLE_MESSAGE : UNREADABLE_PAGES_MESSAGE;
|
|
20064
|
+
}
|
|
20065
|
+
onDrop(group, event) {
|
|
20066
|
+
this.store.movePageWithinGroup({
|
|
20067
|
+
groupKey: group.groupKey,
|
|
20068
|
+
pageIds: group.pages.map((page) => page.id),
|
|
20069
|
+
previousIndex: event.previousIndex,
|
|
20070
|
+
currentIndex: event.currentIndex
|
|
20071
|
+
});
|
|
20072
|
+
}
|
|
20073
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergePageListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
20074
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: DbxPdfMergePageListComponent, isStandalone: true, selector: "dbx-pdf-merge-page-list", inputs: { slotId: { classPropertyName: "slotId", publicName: "slotId", isSignal: true, isRequired: false, transformFunction: null }, showGroupLabels: { classPropertyName: "showGroupLabels", publicName: "showGroupLabels", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "dbx-pdf-merge-page-list d-block" }, ngImport: i0, template: `
|
|
20075
|
+
@if (groupsSignal(); as groups) {
|
|
20076
|
+
@if (groups.length === 0 && unexpandableSignal().length === 0) {
|
|
20077
|
+
<div class="dbx-list-empty-content dbx-hint dbx-p4">No pages yet.</div>
|
|
20078
|
+
} @else {
|
|
20079
|
+
@for (group of groups; track group.groupKey) {
|
|
20080
|
+
<div class="dbx-pdf-merge-page-group">
|
|
20081
|
+
@if (labelForGroup(group); as label) {
|
|
20082
|
+
<div class="dbx-pdf-merge-page-group-label dbx-hint dbx-small">{{ label }}</div>
|
|
20083
|
+
}
|
|
20084
|
+
<div class="dbx-pdf-merge-page-group-items" cdkDropList (cdkDropListDropped)="onDrop(group, $event)">
|
|
20085
|
+
@for (page of group.pages; track page.id) {
|
|
20086
|
+
<dbx-pdf-merge-page [page]="page"></dbx-pdf-merge-page>
|
|
20087
|
+
}
|
|
20088
|
+
</div>
|
|
20089
|
+
</div>
|
|
20090
|
+
}
|
|
20091
|
+
}
|
|
20092
|
+
}
|
|
20093
|
+
@for (entry of unexpandableSignal(); track entry.id) {
|
|
20094
|
+
<div class="dbx-pdf-merge-page-unexpandable">
|
|
20095
|
+
<mat-icon class="dbx-pdf-merge-page-unexpandable-icon">lock</mat-icon>
|
|
20096
|
+
<span class="dbx-pdf-merge-page-unexpandable-name dbx-text-truncate" [title]="entry.name">{{ entry.name }}</span>
|
|
20097
|
+
<span class="dbx-hint dbx-small">{{ messageForUnexpandable(entry) }}</span>
|
|
20098
|
+
</div>
|
|
20099
|
+
}
|
|
20100
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: DbxPdfMergePageComponent, selector: "dbx-pdf-merge-page", inputs: ["page"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
20101
|
+
}
|
|
20102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergePageListComponent, decorators: [{
|
|
20103
|
+
type: Component,
|
|
20104
|
+
args: [{
|
|
20105
|
+
selector: 'dbx-pdf-merge-page-list',
|
|
20106
|
+
template: `
|
|
20107
|
+
@if (groupsSignal(); as groups) {
|
|
20108
|
+
@if (groups.length === 0 && unexpandableSignal().length === 0) {
|
|
20109
|
+
<div class="dbx-list-empty-content dbx-hint dbx-p4">No pages yet.</div>
|
|
20110
|
+
} @else {
|
|
20111
|
+
@for (group of groups; track group.groupKey) {
|
|
20112
|
+
<div class="dbx-pdf-merge-page-group">
|
|
20113
|
+
@if (labelForGroup(group); as label) {
|
|
20114
|
+
<div class="dbx-pdf-merge-page-group-label dbx-hint dbx-small">{{ label }}</div>
|
|
20115
|
+
}
|
|
20116
|
+
<div class="dbx-pdf-merge-page-group-items" cdkDropList (cdkDropListDropped)="onDrop(group, $event)">
|
|
20117
|
+
@for (page of group.pages; track page.id) {
|
|
20118
|
+
<dbx-pdf-merge-page [page]="page"></dbx-pdf-merge-page>
|
|
20119
|
+
}
|
|
20120
|
+
</div>
|
|
20121
|
+
</div>
|
|
20122
|
+
}
|
|
20123
|
+
}
|
|
20124
|
+
}
|
|
20125
|
+
@for (entry of unexpandableSignal(); track entry.id) {
|
|
20126
|
+
<div class="dbx-pdf-merge-page-unexpandable">
|
|
20127
|
+
<mat-icon class="dbx-pdf-merge-page-unexpandable-icon">lock</mat-icon>
|
|
20128
|
+
<span class="dbx-pdf-merge-page-unexpandable-name dbx-text-truncate" [title]="entry.name">{{ entry.name }}</span>
|
|
20129
|
+
<span class="dbx-hint dbx-small">{{ messageForUnexpandable(entry) }}</span>
|
|
20130
|
+
</div>
|
|
20131
|
+
}
|
|
20132
|
+
`,
|
|
20133
|
+
host: {
|
|
20134
|
+
class: 'dbx-pdf-merge-page-list d-block'
|
|
20135
|
+
},
|
|
20136
|
+
imports: [CdkDropList, MatIconModule, DbxPdfMergePageComponent],
|
|
20137
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
20138
|
+
standalone: true
|
|
20139
|
+
}]
|
|
20140
|
+
}], propDecorators: { slotId: [{ type: i0.Input, args: [{ isSignal: true, alias: "slotId", required: false }] }], showGroupLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGroupLabels", required: false }] }] } });
|
|
20141
|
+
|
|
18943
20142
|
/**
|
|
18944
20143
|
* Renders a PDF in a fixed-height embed container. Accepts either a URL or a `Blob` source.
|
|
18945
20144
|
*
|
|
@@ -19026,6 +20225,8 @@ class DbxPdfMergeEditorComponent {
|
|
|
19026
20225
|
downloadButton = input(...(ngDevMode ? [undefined, { debugName: "downloadButton" }] : /* istanbul ignore next */ []));
|
|
19027
20226
|
showAddFiles = input(...(ngDevMode ? [undefined, { debugName: "showAddFiles" }] : /* istanbul ignore next */ []));
|
|
19028
20227
|
showFileList = input(...(ngDevMode ? [undefined, { debugName: "showFileList" }] : /* istanbul ignore next */ []));
|
|
20228
|
+
pageEditing = input(...(ngDevMode ? [undefined, { debugName: "pageEditing" }] : /* istanbul ignore next */ []));
|
|
20229
|
+
sidecar = input(...(ngDevMode ? [undefined, { debugName: "sidecar" }] : /* istanbul ignore next */ []));
|
|
19029
20230
|
/**
|
|
19030
20231
|
* Bundles every editor option into one object (see {@link DbxPdfMergeEditorConfig}). Individual inputs override the matching field here, which in turn overrides the workspace-wide {@link DBX_PDF_MERGE_EDITOR_CONFIG} token.
|
|
19031
20232
|
*/
|
|
@@ -19039,6 +20240,11 @@ class DbxPdfMergeEditorComponent {
|
|
|
19039
20240
|
* Store-level encryption handling default pushed by {@link DbxPdfMergeEditorStoreDirective}. Resolved between the editor's own `config` input and the workspace-wide token in {@link effectiveConfigSignal}.
|
|
19040
20241
|
*/
|
|
19041
20242
|
storeEncryptedHandlingSignal = toSignal(this.store.encryptedHandling$, { initialValue: undefined });
|
|
20243
|
+
/**
|
|
20244
|
+
* Store-level page-editing and sidecar defaults pushed by {@link DbxPdfMergeEditorStoreDirective}. These read the store's raw (undefaulted) settings so an unset store value still falls through to the workspace-wide token in {@link effectiveConfigSignal}.
|
|
20245
|
+
*/
|
|
20246
|
+
storePageEditingSignal = toSignal(this.store.pageEditingSetting$, { initialValue: undefined });
|
|
20247
|
+
storeSidecarSignal = toSignal(this.store.sidecarSetting$, { initialValue: undefined });
|
|
19042
20248
|
/**
|
|
19043
20249
|
* Merged config — the editor's own `config` input wins over the store-level default (for `imageCompression`), which in turn wins over the workspace-wide token. The individual inputs are resolved on top of this object in the per-field `*Signal` computeds below.
|
|
19044
20250
|
*/
|
|
@@ -19047,6 +20253,8 @@ class DbxPdfMergeEditorComponent {
|
|
|
19047
20253
|
const fromToken = this._injectedConfig;
|
|
19048
20254
|
const storeImageCompression = this.storeImageCompressionSignal();
|
|
19049
20255
|
const storeEncryptedHandling = this.storeEncryptedHandlingSignal();
|
|
20256
|
+
const storePageEditing = this.storePageEditingSignal();
|
|
20257
|
+
const storeSidecar = this.storeSidecarSignal();
|
|
19050
20258
|
return {
|
|
19051
20259
|
imageCompression: fromInput?.imageCompression ?? storeImageCompression ?? fromToken?.imageCompression ?? null,
|
|
19052
20260
|
outputSizeLimits: fromInput?.outputSizeLimits ?? fromToken?.outputSizeLimits ?? null,
|
|
@@ -19058,10 +20266,23 @@ class DbxPdfMergeEditorComponent {
|
|
|
19058
20266
|
downloadButton: fromInput?.downloadButton ?? fromToken?.downloadButton,
|
|
19059
20267
|
showAddFiles: fromInput?.showAddFiles ?? fromToken?.showAddFiles,
|
|
19060
20268
|
showFileList: fromInput?.showFileList ?? fromToken?.showFileList,
|
|
19061
|
-
encryptedHandling: fromInput?.encryptedHandling ?? storeEncryptedHandling ?? fromToken?.encryptedHandling ?? DEFAULT_DBX_PDF_MERGE_ENCRYPTED_HANDLING
|
|
20269
|
+
encryptedHandling: fromInput?.encryptedHandling ?? storeEncryptedHandling ?? fromToken?.encryptedHandling ?? DEFAULT_DBX_PDF_MERGE_ENCRYPTED_HANDLING,
|
|
20270
|
+
pageEditing: fromInput?.pageEditing ?? storePageEditing ?? fromToken?.pageEditing ?? DEFAULT_DBX_PDF_MERGE_PAGE_EDITING,
|
|
20271
|
+
sidecar: fromInput?.sidecar ?? storeSidecar ?? fromToken?.sidecar ?? DEFAULT_DBX_PDF_MERGE_SIDECAR
|
|
19062
20272
|
};
|
|
19063
20273
|
}, ...(ngDevMode ? [{ debugName: "effectiveConfigSignal" }] : /* istanbul ignore next */ []));
|
|
19064
20274
|
encryptedHandlingSignal = computed(() => this.effectiveConfigSignal().encryptedHandling ?? DEFAULT_DBX_PDF_MERGE_ENCRYPTED_HANDLING, ...(ngDevMode ? [{ debugName: "encryptedHandlingSignal" }] : /* istanbul ignore next */ []));
|
|
20275
|
+
/**
|
|
20276
|
+
* Whether page editing is active. When `false` the editor renders its original file list and no page machinery is instantiated at all.
|
|
20277
|
+
*/
|
|
20278
|
+
pageEditingSignal = computed(() => {
|
|
20279
|
+
const config = this.effectiveConfigSignal();
|
|
20280
|
+
return this.pageEditing() ?? config.pageEditing ?? DEFAULT_DBX_PDF_MERGE_PAGE_EDITING;
|
|
20281
|
+
}, ...(ngDevMode ? [{ debugName: "pageEditingSignal" }] : /* istanbul ignore next */ []));
|
|
20282
|
+
sidecarSignal = computed(() => {
|
|
20283
|
+
const config = this.effectiveConfigSignal();
|
|
20284
|
+
return this.sidecar() ?? config.sidecar ?? DEFAULT_DBX_PDF_MERGE_SIDECAR;
|
|
20285
|
+
}, ...(ngDevMode ? [{ debugName: "sidecarSignal" }] : /* istanbul ignore next */ []));
|
|
19065
20286
|
imageCompressionConfigSignal = computed(() => this.effectiveConfigSignal().imageCompression, ...(ngDevMode ? [{ debugName: "imageCompressionConfigSignal" }] : /* istanbul ignore next */ []));
|
|
19066
20287
|
outputSizeLimitsSignal = computed(() => this.effectiveConfigSignal().outputSizeLimits, ...(ngDevMode ? [{ debugName: "outputSizeLimitsSignal" }] : /* istanbul ignore next */ []));
|
|
19067
20288
|
// Per-field resolution: dedicated input → config (input/token) → default. The config signal is
|
|
@@ -19102,6 +20323,10 @@ class DbxPdfMergeEditorComponent {
|
|
|
19102
20323
|
errorBytesSignal = computed(() => this.outputSizeLimitsSignal()?.errorBytes, ...(ngDevMode ? [{ debugName: "errorBytesSignal" }] : /* istanbul ignore next */ []));
|
|
19103
20324
|
hasReadyEntriesSignal = toSignal(this.store.hasReadyEntries$, { initialValue: false });
|
|
19104
20325
|
entryCountSignal = toSignal(this.store.entryCount$, { initialValue: 0 });
|
|
20326
|
+
/**
|
|
20327
|
+
* Number of pages that will actually reach the output — the page plan minus anything marked for removal. Only surfaced while page editing is enabled.
|
|
20328
|
+
*/
|
|
20329
|
+
mergeablePageCountSignal = toSignal(this.store.mergeablePageCount$, { initialValue: 0 });
|
|
19105
20330
|
/**
|
|
19106
20331
|
* Mirrors {@link DbxPdfMergeEditorStore.focusActive$} — `true` while `encryptedHandling === 'focus'` and at least one ready encrypted entry exists. Drives the encrypted-PDF focus banner.
|
|
19107
20332
|
*/
|
|
@@ -19171,6 +20396,14 @@ class DbxPdfMergeEditorComponent {
|
|
|
19171
20396
|
const handling = this.encryptedHandlingSignal();
|
|
19172
20397
|
this.store.setEncryptedHandling(handling);
|
|
19173
20398
|
});
|
|
20399
|
+
effect(() => {
|
|
20400
|
+
const pageEditing = this.pageEditingSignal();
|
|
20401
|
+
this.store.setPageEditing(pageEditing);
|
|
20402
|
+
});
|
|
20403
|
+
effect(() => {
|
|
20404
|
+
const sidecar = this.sidecarSignal();
|
|
20405
|
+
this.store.setSidecar(sidecar);
|
|
20406
|
+
});
|
|
19174
20407
|
}
|
|
19175
20408
|
async onFiles(event) {
|
|
19176
20409
|
const accepted = event.matchResult.accepted;
|
|
@@ -19213,18 +20446,20 @@ class DbxPdfMergeEditorComponent {
|
|
|
19213
20446
|
});
|
|
19214
20447
|
}
|
|
19215
20448
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19216
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: DbxPdfMergeEditorComponent, isStandalone: true, selector: "dbx-pdf-merge-editor", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, showDownloadButton: { classPropertyName: "showDownloadButton", publicName: "showDownloadButton", isSignal: true, isRequired: false, transformFunction: null }, showPreviewButton: { classPropertyName: "showPreviewButton", publicName: "showPreviewButton", isSignal: true, isRequired: false, transformFunction: null }, downloadButton: { classPropertyName: "downloadButton", publicName: "downloadButton", isSignal: true, isRequired: false, transformFunction: null }, showAddFiles: { classPropertyName: "showAddFiles", publicName: "showAddFiles", isSignal: true, isRequired: false, transformFunction: null }, showFileList: { classPropertyName: "showFileList", publicName: "showFileList", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { entriesChanged: "entriesChanged" }, host: { classAttribute: "dbx-pdf-merge-editor d-block" }, ngImport: i0, template: "@if (showAddFilesSignal()) {\n <div class=\"dbx-pdf-merge-editor-upload d-block dbx-mb3\">\n <dbx-file-upload [accept]=\"acceptSignal()\" [multiple]=\"multipleSignal()\" (filesChanged)=\"onFiles($event)\" [hint]=\"'Drop PDFs or images here, or click to browse'\" [text]=\"'Add files'\" icon=\"upload_file\"></dbx-file-upload>\n </div>\n}\n<ng-content></ng-content>\n@if (showFileListSignal()) {\n <dbx-pdf-merge-list></dbx-pdf-merge-list>\n}\n@if (focusActiveSignal()) {\n <div class=\"dbx-pdf-merge-editor-encrypted-banner\">\n <mat-icon>lock</mat-icon>\n <span>Encrypted PDF detected \u2014 only the encrypted file will be used; other files are ignored.</span>\n </div>\n}\n@switch (outputSizeStateSignal()) {\n @case ('warn') {\n <div class=\"dbx-pdf-merge-editor-size-warning\">\n <mat-icon>warning</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }} \u2014 above the recommended {{ formattedWarnLimitSignal() }} limit.</span>\n </div>\n }\n @case ('error') {\n <div class=\"dbx-pdf-merge-editor-size-error\">\n <mat-icon>error</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }}, exceeds the {{ formattedErrorLimitSignal() }} limit. Remove or compress files to continue.</span>\n </div>\n }\n}\n<div class=\"dbx-pdf-merge-editor-actions\">\n <span class=\"dbx-hint dbx-small\">{{ entryCountSignal() }} file(s)</span>\n <span class=\"dbx-spacer\"></span>\n <dbx-button text=\"Clear\" icon=\"delete\" [disabled]=\"entryCountSignal() === 0\" (buttonClick)=\"onClear()\"></dbx-button>\n @if (showPreviewButtonSignal()) {\n <dbx-button text=\"Preview\" icon=\"picture_as_pdf\" [disabled]=\"!canMergeSignal()\" (buttonClick)=\"onPreview()\"></dbx-button>\n }\n @if (showDownloadButtonSignal() && canMergeSignal()) {\n <dbx-download-blob-button [config]=\"downloadConfigSignal()\"></dbx-download-blob-button>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "customContent", "allowClickPropagation", "mode"] }, { kind: "component", type: DbxFileUploadComponent, selector: "dbx-file-upload", inputs: ["config", "buttonStyle", "buttonDisplay", "mode", "text", "icon", "hint", "clickAreaToUpload"], outputs: ["filesChanged"] }, { kind: "component", type: DbxDownloadBlobButtonComponent, selector: "dbx-download-blob-button", inputs: ["config"] }, { kind: "component", type: DbxPdfMergeListComponent, selector: "dbx-pdf-merge-list" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
20449
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: DbxPdfMergeEditorComponent, isStandalone: true, selector: "dbx-pdf-merge-editor", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, showDownloadButton: { classPropertyName: "showDownloadButton", publicName: "showDownloadButton", isSignal: true, isRequired: false, transformFunction: null }, showPreviewButton: { classPropertyName: "showPreviewButton", publicName: "showPreviewButton", isSignal: true, isRequired: false, transformFunction: null }, downloadButton: { classPropertyName: "downloadButton", publicName: "downloadButton", isSignal: true, isRequired: false, transformFunction: null }, showAddFiles: { classPropertyName: "showAddFiles", publicName: "showAddFiles", isSignal: true, isRequired: false, transformFunction: null }, showFileList: { classPropertyName: "showFileList", publicName: "showFileList", isSignal: true, isRequired: false, transformFunction: null }, pageEditing: { classPropertyName: "pageEditing", publicName: "pageEditing", isSignal: true, isRequired: false, transformFunction: null }, sidecar: { classPropertyName: "sidecar", publicName: "sidecar", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { entriesChanged: "entriesChanged" }, host: { classAttribute: "dbx-pdf-merge-editor d-block" }, ngImport: i0, template: "@if (showAddFilesSignal()) {\n <div class=\"dbx-pdf-merge-editor-upload d-block dbx-mb3\">\n <dbx-file-upload [accept]=\"acceptSignal()\" [multiple]=\"multipleSignal()\" (filesChanged)=\"onFiles($event)\" [hint]=\"'Drop PDFs or images here, or click to browse'\" [text]=\"'Add files'\" icon=\"upload_file\"></dbx-file-upload>\n </div>\n}\n<ng-content></ng-content>\n@if (showFileListSignal()) {\n @if (pageEditingSignal()) {\n <dbx-pdf-merge-page-list></dbx-pdf-merge-page-list>\n } @else {\n <dbx-pdf-merge-list></dbx-pdf-merge-list>\n }\n}\n@if (focusActiveSignal()) {\n <div class=\"dbx-pdf-merge-editor-encrypted-banner\">\n <mat-icon>lock</mat-icon>\n <span>Encrypted PDF detected \u2014 only the encrypted file will be used; other files are ignored.</span>\n </div>\n}\n@switch (outputSizeStateSignal()) {\n @case ('warn') {\n <div class=\"dbx-pdf-merge-editor-size-warning\">\n <mat-icon>warning</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }} \u2014 above the recommended {{ formattedWarnLimitSignal() }} limit.</span>\n </div>\n }\n @case ('error') {\n <div class=\"dbx-pdf-merge-editor-size-error\">\n <mat-icon>error</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }}, exceeds the {{ formattedErrorLimitSignal() }} limit. Remove or compress files to continue.</span>\n </div>\n }\n}\n<div class=\"dbx-pdf-merge-editor-actions\">\n <span class=\"dbx-hint dbx-small\">{{ entryCountSignal() }} file(s)</span>\n @if (pageEditingSignal()) {\n <span class=\"dbx-hint dbx-small\">{{ mergeablePageCountSignal() }} page(s)</span>\n }\n <span class=\"dbx-spacer\"></span>\n <dbx-button text=\"Clear\" icon=\"delete\" [disabled]=\"entryCountSignal() === 0\" (buttonClick)=\"onClear()\"></dbx-button>\n @if (showPreviewButtonSignal()) {\n <dbx-button text=\"Preview\" icon=\"picture_as_pdf\" [disabled]=\"!canMergeSignal()\" (buttonClick)=\"onPreview()\"></dbx-button>\n }\n @if (showDownloadButtonSignal() && canMergeSignal()) {\n <dbx-download-blob-button [config]=\"downloadConfigSignal()\"></dbx-download-blob-button>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "customContent", "allowClickPropagation", "mode"] }, { kind: "component", type: DbxFileUploadComponent, selector: "dbx-file-upload", inputs: ["config", "buttonStyle", "buttonDisplay", "mode", "text", "icon", "hint", "clickAreaToUpload"], outputs: ["filesChanged"] }, { kind: "component", type: DbxDownloadBlobButtonComponent, selector: "dbx-download-blob-button", inputs: ["config"] }, { kind: "component", type: DbxPdfMergeListComponent, selector: "dbx-pdf-merge-list" }, { kind: "component", type: DbxPdfMergePageListComponent, selector: "dbx-pdf-merge-page-list", inputs: ["slotId", "showGroupLabels"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19217
20450
|
}
|
|
19218
20451
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorComponent, decorators: [{
|
|
19219
20452
|
type: Component,
|
|
19220
20453
|
args: [{ selector: 'dbx-pdf-merge-editor', host: {
|
|
19221
20454
|
class: 'dbx-pdf-merge-editor d-block'
|
|
19222
|
-
}, imports: [MatIconModule, DbxButtonComponent, DbxFileUploadComponent, DbxDownloadBlobButtonComponent, DbxPdfMergeListComponent], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "@if (showAddFilesSignal()) {\n <div class=\"dbx-pdf-merge-editor-upload d-block dbx-mb3\">\n <dbx-file-upload [accept]=\"acceptSignal()\" [multiple]=\"multipleSignal()\" (filesChanged)=\"onFiles($event)\" [hint]=\"'Drop PDFs or images here, or click to browse'\" [text]=\"'Add files'\" icon=\"upload_file\"></dbx-file-upload>\n </div>\n}\n<ng-content></ng-content>\n@if (showFileListSignal()) {\n <dbx-pdf-merge-list></dbx-pdf-merge-list>\n}\n@if (focusActiveSignal()) {\n <div class=\"dbx-pdf-merge-editor-encrypted-banner\">\n <mat-icon>lock</mat-icon>\n <span>Encrypted PDF detected \u2014 only the encrypted file will be used; other files are ignored.</span>\n </div>\n}\n@switch (outputSizeStateSignal()) {\n @case ('warn') {\n <div class=\"dbx-pdf-merge-editor-size-warning\">\n <mat-icon>warning</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }} \u2014 above the recommended {{ formattedWarnLimitSignal() }} limit.</span>\n </div>\n }\n @case ('error') {\n <div class=\"dbx-pdf-merge-editor-size-error\">\n <mat-icon>error</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }}, exceeds the {{ formattedErrorLimitSignal() }} limit. Remove or compress files to continue.</span>\n </div>\n }\n}\n<div class=\"dbx-pdf-merge-editor-actions\">\n <span class=\"dbx-hint dbx-small\">{{ entryCountSignal() }} file(s)</span>\n <span class=\"dbx-spacer\"></span>\n <dbx-button text=\"Clear\" icon=\"delete\" [disabled]=\"entryCountSignal() === 0\" (buttonClick)=\"onClear()\"></dbx-button>\n @if (showPreviewButtonSignal()) {\n <dbx-button text=\"Preview\" icon=\"picture_as_pdf\" [disabled]=\"!canMergeSignal()\" (buttonClick)=\"onPreview()\"></dbx-button>\n }\n @if (showDownloadButtonSignal() && canMergeSignal()) {\n <dbx-download-blob-button [config]=\"downloadConfigSignal()\"></dbx-download-blob-button>\n }\n</div>\n" }]
|
|
19223
|
-
}], ctorParameters: () => [], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], showDownloadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDownloadButton", required: false }] }], showPreviewButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPreviewButton", required: false }] }], downloadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "downloadButton", required: false }] }], showAddFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAddFiles", required: false }] }], showFileList: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFileList", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], entriesChanged: [{ type: i0.Output, args: ["entriesChanged"] }] } });
|
|
20455
|
+
}, imports: [MatIconModule, DbxButtonComponent, DbxFileUploadComponent, DbxDownloadBlobButtonComponent, DbxPdfMergeListComponent, DbxPdfMergePageListComponent], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "@if (showAddFilesSignal()) {\n <div class=\"dbx-pdf-merge-editor-upload d-block dbx-mb3\">\n <dbx-file-upload [accept]=\"acceptSignal()\" [multiple]=\"multipleSignal()\" (filesChanged)=\"onFiles($event)\" [hint]=\"'Drop PDFs or images here, or click to browse'\" [text]=\"'Add files'\" icon=\"upload_file\"></dbx-file-upload>\n </div>\n}\n<ng-content></ng-content>\n@if (showFileListSignal()) {\n @if (pageEditingSignal()) {\n <dbx-pdf-merge-page-list></dbx-pdf-merge-page-list>\n } @else {\n <dbx-pdf-merge-list></dbx-pdf-merge-list>\n }\n}\n@if (focusActiveSignal()) {\n <div class=\"dbx-pdf-merge-editor-encrypted-banner\">\n <mat-icon>lock</mat-icon>\n <span>Encrypted PDF detected \u2014 only the encrypted file will be used; other files are ignored.</span>\n </div>\n}\n@switch (outputSizeStateSignal()) {\n @case ('warn') {\n <div class=\"dbx-pdf-merge-editor-size-warning\">\n <mat-icon>warning</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }} \u2014 above the recommended {{ formattedWarnLimitSignal() }} limit.</span>\n </div>\n }\n @case ('error') {\n <div class=\"dbx-pdf-merge-editor-size-error\">\n <mat-icon>error</mat-icon>\n <span>Merged file is {{ formattedOutputSizeSignal() }}, exceeds the {{ formattedErrorLimitSignal() }} limit. Remove or compress files to continue.</span>\n </div>\n }\n}\n<div class=\"dbx-pdf-merge-editor-actions\">\n <span class=\"dbx-hint dbx-small\">{{ entryCountSignal() }} file(s)</span>\n @if (pageEditingSignal()) {\n <span class=\"dbx-hint dbx-small\">{{ mergeablePageCountSignal() }} page(s)</span>\n }\n <span class=\"dbx-spacer\"></span>\n <dbx-button text=\"Clear\" icon=\"delete\" [disabled]=\"entryCountSignal() === 0\" (buttonClick)=\"onClear()\"></dbx-button>\n @if (showPreviewButtonSignal()) {\n <dbx-button text=\"Preview\" icon=\"picture_as_pdf\" [disabled]=\"!canMergeSignal()\" (buttonClick)=\"onPreview()\"></dbx-button>\n }\n @if (showDownloadButtonSignal() && canMergeSignal()) {\n <dbx-download-blob-button [config]=\"downloadConfigSignal()\"></dbx-download-blob-button>\n }\n</div>\n" }]
|
|
20456
|
+
}], ctorParameters: () => [], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], showDownloadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDownloadButton", required: false }] }], showPreviewButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPreviewButton", required: false }] }], downloadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "downloadButton", required: false }] }], showAddFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAddFiles", required: false }] }], showFileList: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFileList", required: false }] }], pageEditing: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageEditing", required: false }] }], sidecar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidecar", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], entriesChanged: [{ type: i0.Output, args: ["entriesChanged"] }] } });
|
|
19224
20457
|
|
|
19225
20458
|
/**
|
|
19226
20459
|
* Directive that registers as the active {@link DbxPdfMergeEditorValidator} on a {@link DbxPdfMergeEditorStore} and gates merge emissions on the readiness of every registered slot. {@link DbxPdfMergeEditorFileUploadComponent} instances walk the injector tree, find this directive, and call {@link registerSlot}/{@link unregisterSlot} during their own lifecycle.
|
|
19227
20460
|
*
|
|
20461
|
+
* This slot set is intentionally separate from {@link DbxPdfMergeEditorStore.registeredSlotIds$}, and the two should not be merged. This one is keyed by object identity because it exists solely to `combineLatest` each slot's `isValid$`; the store's is keyed by slot id, answers "which sections does this editor declare", and must be populated whether or not a validator is present — which is why it cannot live on this optional directive.
|
|
20462
|
+
*
|
|
19228
20463
|
* @example
|
|
19229
20464
|
* ```html
|
|
19230
20465
|
* <dbx-pdf-merge-editor [showAddFiles]="false" [showFileList]="false">
|
|
@@ -19310,6 +20545,10 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19310
20545
|
_validator = inject(DbxPdfMergeEditorFileUploadValidatorDirective, { optional: true });
|
|
19311
20546
|
_injectedConfig = inject(DBX_PDF_MERGE_EDITOR_CONFIG, { optional: true });
|
|
19312
20547
|
_preserveEntriesOnDestroy = inject(DBX_PDF_MERGE_EDITOR_PRESERVE_ENTRIES_ON_SLOT_DESTROY, { optional: true }) ?? false;
|
|
20548
|
+
/**
|
|
20549
|
+
* The slot id this component registered with the store, captured at registration time so destroy deregisters the same key. `null` before {@link ngOnInit} and after teardown.
|
|
20550
|
+
*/
|
|
20551
|
+
_registeredSlotId = null;
|
|
19313
20552
|
slotId = input.required(...(ngDevMode ? [{ debugName: "slotId" }] : /* istanbul ignore next */ []));
|
|
19314
20553
|
config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : /* istanbul ignore next */ []));
|
|
19315
20554
|
acceptSignal = computed(() => this.config()?.accept ?? DEFAULT_PDF_MERGE_ACCEPT, ...(ngDevMode ? [{ debugName: "acceptSignal" }] : /* istanbul ignore next */ []));
|
|
@@ -19345,6 +20584,14 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19345
20584
|
*/
|
|
19346
20585
|
ownedEntries$ = toObservable(this.slotId).pipe(switchMap((slotId) => this.store.entriesForSlotId$(slotId)), shareReplay(1));
|
|
19347
20586
|
ownedEntriesSignal = toSignal(this.ownedEntries$, { initialValue: [] });
|
|
20587
|
+
/**
|
|
20588
|
+
* Whether the shared store has page editing enabled. Drives whether this slot lists its own pages or its own files.
|
|
20589
|
+
*/
|
|
20590
|
+
pageEditingSignal = toSignal(this.store.pageEditing$, { initialValue: false });
|
|
20591
|
+
/**
|
|
20592
|
+
* Pages contributed by this slot's entries. Empty while page editing is disabled.
|
|
20593
|
+
*/
|
|
20594
|
+
ownedPages$ = toObservable(this.slotId).pipe(switchMap((slotId) => this.store.pagesForSlotId$(slotId)), shareReplay(1));
|
|
19348
20595
|
/**
|
|
19349
20596
|
* Whether the slot still has room for more files. Drives visibility of the upload UI — once the slot is at capacity, the uploader is hidden and the user must remove an existing entry to add another.
|
|
19350
20597
|
*/
|
|
@@ -19352,7 +20599,7 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19352
20599
|
/**
|
|
19353
20600
|
* High-level state of the slot — `no_file` when empty, `valid` when owned entries satisfy the slot's thresholds, `invalid` when owned entries fail or are still being validated.
|
|
19354
20601
|
*/
|
|
19355
|
-
state$ = this.ownedEntries
|
|
20602
|
+
state$ = combineLatest([this.ownedEntries$, this.store.pageEditing$, this.ownedPages$]).pipe(map(([entries, pageEditing, pages]) => {
|
|
19356
20603
|
let state;
|
|
19357
20604
|
if (entries.length === 0) {
|
|
19358
20605
|
state = 'no_file';
|
|
@@ -19362,6 +20609,10 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19362
20609
|
const readyCount = entries.filter((entry) => entry.status === 'ready').length;
|
|
19363
20610
|
const minFiles = this.minFilesSignal();
|
|
19364
20611
|
const maxFiles = this.maxFilesSignal();
|
|
20612
|
+
// Only meaningful once the slot actually has pages: an empty list means editing is off, the
|
|
20613
|
+
// entry is still hydrating, or it could not be expanded (encrypted) — none of which are the
|
|
20614
|
+
// user deleting their own content.
|
|
20615
|
+
const allPagesRemoved = pageEditing && pages.length > 0 && pages.every((page) => page.removed);
|
|
19365
20616
|
if (validating) {
|
|
19366
20617
|
state = 'invalid';
|
|
19367
20618
|
}
|
|
@@ -19371,6 +20622,9 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19371
20622
|
else if (maxFiles != null && readyCount > maxFiles) {
|
|
19372
20623
|
state = 'invalid';
|
|
19373
20624
|
}
|
|
20625
|
+
else if (allPagesRemoved) {
|
|
20626
|
+
state = 'invalid';
|
|
20627
|
+
}
|
|
19374
20628
|
else {
|
|
19375
20629
|
state = 'valid';
|
|
19376
20630
|
}
|
|
@@ -19397,9 +20651,26 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19397
20651
|
}), distinctUntilChanged(), shareReplay(1));
|
|
19398
20652
|
isValidSignal = toSignal(this.isValid$, { initialValue: false });
|
|
19399
20653
|
ngOnInit() {
|
|
20654
|
+
// Two registrations, deliberately: the store learns this slot's ID (so consumers like
|
|
20655
|
+
// <dbx-pdf-merge-import> can enumerate the sections this editor declares), while the optional
|
|
20656
|
+
// validator directive tracks the component itself to aggregate `isValid$`. They are separate
|
|
20657
|
+
// because the store is always present while the validator is not, and because the store's
|
|
20658
|
+
// `setValidator` seam exists precisely so it never has to hold slot component references.
|
|
20659
|
+
this._registeredSlotId = this.slotId();
|
|
20660
|
+
this.store.registerSlotId(this._registeredSlotId);
|
|
19400
20661
|
this._validator?.registerSlot(this);
|
|
19401
20662
|
}
|
|
19402
20663
|
ngOnDestroy() {
|
|
20664
|
+
// Unconditional, and deliberately OUTSIDE the `_preserveEntriesOnDestroy` check below: that
|
|
20665
|
+
// token governs whether the slot's ENTRIES survive, not whether the slot is mounted. The
|
|
20666
|
+
// upload dialog sets it to `true` and is torn down on every close, so gating this on it would
|
|
20667
|
+
// leave the store permanently claiming a section that is no longer on screen. The captured id
|
|
20668
|
+
// (rather than a fresh `slotId()` read) guarantees this decrements the same key `ngOnInit`
|
|
20669
|
+
// incremented, and clearing it keeps a repeat destroy from evicting a still-mounted duplicate.
|
|
20670
|
+
if (this._registeredSlotId != null) {
|
|
20671
|
+
this.store.unregisterSlotId(this._registeredSlotId);
|
|
20672
|
+
this._registeredSlotId = null;
|
|
20673
|
+
}
|
|
19403
20674
|
this._validator?.unregisterSlot(this);
|
|
19404
20675
|
// Default behavior: removing a slot from a template (e.g. via `@if`) also drops the slot's
|
|
19405
20676
|
// entries from the store. Opt out by providing `DBX_PDF_MERGE_EDITOR_PRESERVE_ENTRIES_ON_SLOT_DESTROY`
|
|
@@ -19466,14 +20737,18 @@ class DbxPdfMergeEditorFileUploadComponent {
|
|
|
19466
20737
|
}
|
|
19467
20738
|
@if (ownedEntriesSignal(); as owned) {
|
|
19468
20739
|
@if (owned.length > 0) {
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
20740
|
+
@if (pageEditingSignal()) {
|
|
20741
|
+
<dbx-pdf-merge-page-list class="dbx-pdf-merge-editor-file-upload-pages" [slotId]="slotId()"></dbx-pdf-merge-page-list>
|
|
20742
|
+
} @else {
|
|
20743
|
+
<div class="dbx-pdf-merge-editor-file-upload-entries" cdkDropList (cdkDropListDropped)="onDrop($event)">
|
|
20744
|
+
@for (entry of owned; track entry.id) {
|
|
20745
|
+
<dbx-pdf-merge-entry [entry]="entry"></dbx-pdf-merge-entry>
|
|
20746
|
+
}
|
|
20747
|
+
</div>
|
|
20748
|
+
}
|
|
19474
20749
|
}
|
|
19475
20750
|
}
|
|
19476
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "component", type: DbxFileUploadComponent, selector: "dbx-file-upload", inputs: ["config", "buttonStyle", "buttonDisplay", "mode", "text", "icon", "hint", "clickAreaToUpload"], outputs: ["filesChanged"] }, { kind: "component", type: DbxPdfMergeEntryComponent, selector: "dbx-pdf-merge-entry", inputs: ["entry"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
20751
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "component", type: DbxFileUploadComponent, selector: "dbx-file-upload", inputs: ["config", "buttonStyle", "buttonDisplay", "mode", "text", "icon", "hint", "clickAreaToUpload"], outputs: ["filesChanged"] }, { kind: "component", type: DbxPdfMergeEntryComponent, selector: "dbx-pdf-merge-entry", inputs: ["entry"] }, { kind: "component", type: DbxPdfMergePageListComponent, selector: "dbx-pdf-merge-page-list", inputs: ["slotId", "showGroupLabels"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19477
20752
|
}
|
|
19478
20753
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeEditorFileUploadComponent, decorators: [{
|
|
19479
20754
|
type: Component,
|
|
@@ -19491,11 +20766,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
19491
20766
|
}
|
|
19492
20767
|
@if (ownedEntriesSignal(); as owned) {
|
|
19493
20768
|
@if (owned.length > 0) {
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
20769
|
+
@if (pageEditingSignal()) {
|
|
20770
|
+
<dbx-pdf-merge-page-list class="dbx-pdf-merge-editor-file-upload-pages" [slotId]="slotId()"></dbx-pdf-merge-page-list>
|
|
20771
|
+
} @else {
|
|
20772
|
+
<div class="dbx-pdf-merge-editor-file-upload-entries" cdkDropList (cdkDropListDropped)="onDrop($event)">
|
|
20773
|
+
@for (entry of owned; track entry.id) {
|
|
20774
|
+
<dbx-pdf-merge-entry [entry]="entry"></dbx-pdf-merge-entry>
|
|
20775
|
+
}
|
|
20776
|
+
</div>
|
|
20777
|
+
}
|
|
19499
20778
|
}
|
|
19500
20779
|
}
|
|
19501
20780
|
`,
|
|
@@ -19505,7 +20784,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
19505
20784
|
'[class.dbx-pdf-merge-editor-file-upload--valid]': 'stateSignal() === "valid"',
|
|
19506
20785
|
'[class.dbx-pdf-merge-editor-file-upload--no-file]': 'stateSignal() === "no_file"'
|
|
19507
20786
|
},
|
|
19508
|
-
imports: [CdkDropList, DbxFileUploadComponent, DbxPdfMergeEntryComponent],
|
|
20787
|
+
imports: [CdkDropList, DbxFileUploadComponent, DbxPdfMergeEntryComponent, DbxPdfMergePageListComponent],
|
|
19509
20788
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
19510
20789
|
standalone: true
|
|
19511
20790
|
}]
|
|
@@ -19544,6 +20823,224 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
19544
20823
|
}]
|
|
19545
20824
|
}], propDecorators: { targetState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dbxPdfMergeEditorFileUploadHasState", required: false }] }] } });
|
|
19546
20825
|
|
|
20826
|
+
/**
|
|
20827
|
+
* Messages shown for each reason an import can fail.
|
|
20828
|
+
*/
|
|
20829
|
+
const DBX_PDF_MERGE_IMPORT_ERROR_MESSAGES = {
|
|
20830
|
+
unreadable: 'This file could not be read as a PDF. It may be corrupt or password protected.',
|
|
20831
|
+
no_sidecar: 'This PDF has no embedded manifest, so its pages cannot be matched back to sections. Choose a PDF that was exported from this editor.',
|
|
20832
|
+
no_documents: 'This PDF has a manifest, but none of its recorded pages could be found in the file.'
|
|
20833
|
+
};
|
|
20834
|
+
/**
|
|
20835
|
+
* Loads a PDF that was previously exported from the editor with an embedded manifest, and repopulates the editor's slots from it.
|
|
20836
|
+
*
|
|
20837
|
+
* This is the payoff of the sidecar: a completed single-file document goes back to being the set of per-slot documents it was assembled from, so a user can replace or edit one section in place instead of rebuilding the whole file. Every entry currently in the store is replaced.
|
|
20838
|
+
*
|
|
20839
|
+
* **Zero configuration.** The sections this editor accepts are read from the `<dbx-pdf-merge-editor-file-upload>` slots mounted against the shared {@link DbxPdfMergeEditorStore} (see {@link DbxPdfMergeEditorStore.registeredSlotIds$}), so the common case needs no bindings at all. A file naming any other section is rejected rather than partially imported — without that check, entries carrying an unrecognized slot id would import and then render nowhere.
|
|
20840
|
+
*
|
|
20841
|
+
* **Renders nothing when no sections are configured.** This component exists to split a document back into slots, so with no slots mounted (and no explicit {@link expectedSlotIds}) it has nothing to import into and collapses to zero size. Bind {@link enforceExpectedSlots} to `false` for the unusual case of an editor that wants the import affordance without declaring slots.
|
|
20842
|
+
*
|
|
20843
|
+
* @example
|
|
20844
|
+
* ```html
|
|
20845
|
+
* <!-- derives its sections from the slots below it -->
|
|
20846
|
+
* <dbx-pdf-merge-import (imported)="onImported($event)"></dbx-pdf-merge-import>
|
|
20847
|
+
* <dbx-pdf-merge-editor>
|
|
20848
|
+
* <dbx-pdf-merge-editor-file-upload slotId="license"></dbx-pdf-merge-editor-file-upload>
|
|
20849
|
+
* <dbx-pdf-merge-editor-file-upload slotId="cert"></dbx-pdf-merge-editor-file-upload>
|
|
20850
|
+
* </dbx-pdf-merge-editor>
|
|
20851
|
+
* ```
|
|
20852
|
+
*/
|
|
20853
|
+
class DbxPdfMergeImportComponent {
|
|
20854
|
+
store = inject(DbxPdfMergeEditorStore);
|
|
20855
|
+
config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : /* istanbul ignore next */ []));
|
|
20856
|
+
/**
|
|
20857
|
+
* Optional override for the slot ids this editor renders. Leave unbound to derive them from the slots mounted against the store — the zero-configuration default. When set, a file whose manifest names any other slot is rejected rather than partially imported.
|
|
20858
|
+
*
|
|
20859
|
+
* An explicitly bound empty array reads as "this editor deliberately has no sections" and hides the component, the same as having no slots mounted.
|
|
20860
|
+
*/
|
|
20861
|
+
expectedSlotIds = input(...(ngDevMode ? [undefined, { debugName: "expectedSlotIds" }] : /* istanbul ignore next */ []));
|
|
20862
|
+
/**
|
|
20863
|
+
* Whether the imported file's sections must match this editor's. Defaults to `true`.
|
|
20864
|
+
*
|
|
20865
|
+
* Binding `false` is the single escape hatch for an editor with no slots that still wants the import affordance: it both skips the check and keeps the component rendered.
|
|
20866
|
+
*/
|
|
20867
|
+
enforceExpectedSlots = input(true, ...(ngDevMode ? [{ debugName: "enforceExpectedSlots" }] : /* istanbul ignore next */ []));
|
|
20868
|
+
imported = output();
|
|
20869
|
+
importFailed = output();
|
|
20870
|
+
/**
|
|
20871
|
+
* Emits the expected sections the imported file did not fill, when there are any. The import itself still succeeds — a missing section is a partial state the user can fill in.
|
|
20872
|
+
*/
|
|
20873
|
+
missingSlots = output();
|
|
20874
|
+
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
20875
|
+
_result = signal(null, ...(ngDevMode ? [{ debugName: "_result" }] : /* istanbul ignore next */ []));
|
|
20876
|
+
_missingSlotIds = signal([], ...(ngDevMode ? [{ debugName: "_missingSlotIds" }] : /* istanbul ignore next */ []));
|
|
20877
|
+
errorSignal = this._error.asReadonly();
|
|
20878
|
+
resultSignal = this._result.asReadonly();
|
|
20879
|
+
missingSlotIdsSignal = this._missingSlotIds.asReadonly();
|
|
20880
|
+
_registeredSlotIdsSignal = toSignal(this.store.registeredSlotIds$, { initialValue: [] });
|
|
20881
|
+
/**
|
|
20882
|
+
* The sections an imported file is allowed to name, or `null` when no check applies. Resolves the explicit {@link expectedSlotIds} binding over the slots registered with the store.
|
|
20883
|
+
*/
|
|
20884
|
+
effectiveExpectedSlotIdsSignal = computed(() => {
|
|
20885
|
+
const registered = this._registeredSlotIdsSignal();
|
|
20886
|
+
const bound = this.expectedSlotIds();
|
|
20887
|
+
let expected;
|
|
20888
|
+
if (!this.enforceExpectedSlots()) {
|
|
20889
|
+
expected = null;
|
|
20890
|
+
}
|
|
20891
|
+
else if (bound == null) {
|
|
20892
|
+
// An empty registry is "nothing declared yet", not "nothing allowed" — the component hides
|
|
20893
|
+
// rather than rejecting, so a null here never becomes a check that fails every file.
|
|
20894
|
+
expected = registered.length > 0 ? registered : null;
|
|
20895
|
+
}
|
|
20896
|
+
else {
|
|
20897
|
+
expected = bound;
|
|
20898
|
+
}
|
|
20899
|
+
return expected;
|
|
20900
|
+
}, ...(ngDevMode ? [{ debugName: "effectiveExpectedSlotIdsSignal" }] : /* istanbul ignore next */ []));
|
|
20901
|
+
/**
|
|
20902
|
+
* Whether this component has anything to import into. `false` collapses it to nothing — see the class docs.
|
|
20903
|
+
*/
|
|
20904
|
+
activeSignal = computed(() => {
|
|
20905
|
+
const expected = this.effectiveExpectedSlotIdsSignal();
|
|
20906
|
+
return !this.enforceExpectedSlots() || (expected != null && expected.length > 0);
|
|
20907
|
+
}, ...(ngDevMode ? [{ debugName: "activeSignal" }] : /* istanbul ignore next */ []));
|
|
20908
|
+
acceptSignal = computed(() => this.config()?.accept ?? [PDF_MIME_TYPE], ...(ngDevMode ? [{ debugName: "acceptSignal" }] : /* istanbul ignore next */ []));
|
|
20909
|
+
modeSignal = computed(() => this.config()?.mode ?? 'default', ...(ngDevMode ? [{ debugName: "modeSignal" }] : /* istanbul ignore next */ []));
|
|
20910
|
+
labelSignal = computed(() => this.config()?.label, ...(ngDevMode ? [{ debugName: "labelSignal" }] : /* istanbul ignore next */ []));
|
|
20911
|
+
hintSignal = computed(() => this.config()?.hint ?? 'Choose a PDF previously exported from this editor', ...(ngDevMode ? [{ debugName: "hintSignal" }] : /* istanbul ignore next */ []));
|
|
20912
|
+
textSignal = computed(() => this.config()?.text ?? 'Import merged PDF', ...(ngDevMode ? [{ debugName: "textSignal" }] : /* istanbul ignore next */ []));
|
|
20913
|
+
iconSignal = computed(() => this.config()?.icon ?? 'upload_file', ...(ngDevMode ? [{ debugName: "iconSignal" }] : /* istanbul ignore next */ []));
|
|
20914
|
+
successLabelSignal = computed(() => {
|
|
20915
|
+
const result = this._result();
|
|
20916
|
+
let label;
|
|
20917
|
+
if (result == null) {
|
|
20918
|
+
label = null;
|
|
20919
|
+
}
|
|
20920
|
+
else {
|
|
20921
|
+
const sections = result.slotIds.map((slotId) => slotId ?? 'unsectioned').join(', ');
|
|
20922
|
+
label = `Imported ${result.entries.length} section(s): ${sections}.`;
|
|
20923
|
+
}
|
|
20924
|
+
return label;
|
|
20925
|
+
}, ...(ngDevMode ? [{ debugName: "successLabelSignal" }] : /* istanbul ignore next */ []));
|
|
20926
|
+
/**
|
|
20927
|
+
* Warning shown when the imported file left one or more expected sections empty. The import succeeded — this only explains why a slot is still blank.
|
|
20928
|
+
*/
|
|
20929
|
+
missingLabelSignal = computed(() => {
|
|
20930
|
+
const missing = this._missingSlotIds();
|
|
20931
|
+
return missing.length > 0 ? `This PDF did not include section(s): ${missing.join(', ')}.` : null;
|
|
20932
|
+
}, ...(ngDevMode ? [{ debugName: "missingLabelSignal" }] : /* istanbul ignore next */ []));
|
|
20933
|
+
async onFiles(event) {
|
|
20934
|
+
const file = event.matchResult.accepted[0];
|
|
20935
|
+
if (file != null) {
|
|
20936
|
+
this._error.set(null);
|
|
20937
|
+
this._result.set(null);
|
|
20938
|
+
this._missingSlotIds.set([]);
|
|
20939
|
+
// Delegates to the store so the picker and a programmatic import share one implementation
|
|
20940
|
+
// and one set of error semantics. The slot check is opt-in on the store, so the component's
|
|
20941
|
+
// own resolved expectation is what preserves the picker's behavior. The returned state is
|
|
20942
|
+
// used rather than `importState$` so these outputs only ever describe a picked file.
|
|
20943
|
+
const state = await this.store.importMergedPdf({ source: file, expectedSlotIds: this.effectiveExpectedSlotIdsSignal() });
|
|
20944
|
+
if (state.status === 'failed') {
|
|
20945
|
+
this.fail(this.messageForFailure(state));
|
|
20946
|
+
}
|
|
20947
|
+
else if (state.result != null) {
|
|
20948
|
+
const { result } = state;
|
|
20949
|
+
this._result.set(result);
|
|
20950
|
+
this._missingSlotIds.set(result.missingSlotIds);
|
|
20951
|
+
this.imported.emit(result);
|
|
20952
|
+
if (result.missingSlotIds.length > 0) {
|
|
20953
|
+
this.missingSlots.emit(result.missingSlotIds);
|
|
20954
|
+
}
|
|
20955
|
+
}
|
|
20956
|
+
}
|
|
20957
|
+
}
|
|
20958
|
+
messageForFailure(state) {
|
|
20959
|
+
const unexpected = state.unexpectedSlotIds ?? [];
|
|
20960
|
+
let message;
|
|
20961
|
+
if (state.error === 'unexpected_slots') {
|
|
20962
|
+
message = `This PDF contains section(s) this editor does not have: ${unexpected.map((slotId) => slotId ?? 'unsectioned').join(', ')}.`;
|
|
20963
|
+
}
|
|
20964
|
+
else {
|
|
20965
|
+
message = DBX_PDF_MERGE_IMPORT_ERROR_MESSAGES[state.error ?? 'unreadable'];
|
|
20966
|
+
}
|
|
20967
|
+
return message;
|
|
20968
|
+
}
|
|
20969
|
+
fail(message) {
|
|
20970
|
+
this._error.set(message);
|
|
20971
|
+
this.importFailed.emit(message);
|
|
20972
|
+
}
|
|
20973
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeImportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
20974
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: DbxPdfMergeImportComponent, isStandalone: true, selector: "dbx-pdf-merge-import", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, expectedSlotIds: { classPropertyName: "expectedSlotIds", publicName: "expectedSlotIds", isSignal: true, isRequired: false, transformFunction: null }, enforceExpectedSlots: { classPropertyName: "enforceExpectedSlots", publicName: "enforceExpectedSlots", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imported: "imported", importFailed: "importFailed", missingSlots: "missingSlots" }, host: { properties: { "class.d-block": "activeSignal()", "class.dbx-mb3": "activeSignal()" }, classAttribute: "dbx-pdf-merge-import" }, ngImport: i0, template: `
|
|
20975
|
+
@if (activeSignal()) {
|
|
20976
|
+
@if (labelSignal(); as label) {
|
|
20977
|
+
<div class="dbx-pdf-merge-import-label">{{ label }}</div>
|
|
20978
|
+
}
|
|
20979
|
+
<dbx-file-upload [accept]="acceptSignal()" [multiple]="false" [mode]="modeSignal()" [hint]="hintSignal()" [text]="textSignal()" [icon]="iconSignal()" (filesChanged)="onFiles($event)"></dbx-file-upload>
|
|
20980
|
+
@if (errorSignal(); as error) {
|
|
20981
|
+
<div class="dbx-pdf-merge-import-error">
|
|
20982
|
+
<mat-icon class="dbx-pdf-merge-import-icon">error</mat-icon>
|
|
20983
|
+
<span>{{ error }}</span>
|
|
20984
|
+
</div>
|
|
20985
|
+
}
|
|
20986
|
+
@if (successLabelSignal(); as success) {
|
|
20987
|
+
<div class="dbx-pdf-merge-import-success">
|
|
20988
|
+
<mat-icon class="dbx-pdf-merge-import-icon">check_circle</mat-icon>
|
|
20989
|
+
<span>{{ success }}</span>
|
|
20990
|
+
</div>
|
|
20991
|
+
}
|
|
20992
|
+
@if (missingLabelSignal(); as missing) {
|
|
20993
|
+
<div class="dbx-pdf-merge-import-warning">
|
|
20994
|
+
<mat-icon class="dbx-pdf-merge-import-icon">warning</mat-icon>
|
|
20995
|
+
<span>{{ missing }}</span>
|
|
20996
|
+
</div>
|
|
20997
|
+
}
|
|
20998
|
+
}
|
|
20999
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: DbxFileUploadComponent, selector: "dbx-file-upload", inputs: ["config", "buttonStyle", "buttonDisplay", "mode", "text", "icon", "hint", "clickAreaToUpload"], outputs: ["filesChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
21000
|
+
}
|
|
21001
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeImportComponent, decorators: [{
|
|
21002
|
+
type: Component,
|
|
21003
|
+
args: [{
|
|
21004
|
+
selector: 'dbx-pdf-merge-import',
|
|
21005
|
+
template: `
|
|
21006
|
+
@if (activeSignal()) {
|
|
21007
|
+
@if (labelSignal(); as label) {
|
|
21008
|
+
<div class="dbx-pdf-merge-import-label">{{ label }}</div>
|
|
21009
|
+
}
|
|
21010
|
+
<dbx-file-upload [accept]="acceptSignal()" [multiple]="false" [mode]="modeSignal()" [hint]="hintSignal()" [text]="textSignal()" [icon]="iconSignal()" (filesChanged)="onFiles($event)"></dbx-file-upload>
|
|
21011
|
+
@if (errorSignal(); as error) {
|
|
21012
|
+
<div class="dbx-pdf-merge-import-error">
|
|
21013
|
+
<mat-icon class="dbx-pdf-merge-import-icon">error</mat-icon>
|
|
21014
|
+
<span>{{ error }}</span>
|
|
21015
|
+
</div>
|
|
21016
|
+
}
|
|
21017
|
+
@if (successLabelSignal(); as success) {
|
|
21018
|
+
<div class="dbx-pdf-merge-import-success">
|
|
21019
|
+
<mat-icon class="dbx-pdf-merge-import-icon">check_circle</mat-icon>
|
|
21020
|
+
<span>{{ success }}</span>
|
|
21021
|
+
</div>
|
|
21022
|
+
}
|
|
21023
|
+
@if (missingLabelSignal(); as missing) {
|
|
21024
|
+
<div class="dbx-pdf-merge-import-warning">
|
|
21025
|
+
<mat-icon class="dbx-pdf-merge-import-icon">warning</mat-icon>
|
|
21026
|
+
<span>{{ missing }}</span>
|
|
21027
|
+
</div>
|
|
21028
|
+
}
|
|
21029
|
+
}
|
|
21030
|
+
`,
|
|
21031
|
+
host: {
|
|
21032
|
+
class: 'dbx-pdf-merge-import',
|
|
21033
|
+
// Collapsed rather than merely emptied while inactive: without `d-block` the host is an empty
|
|
21034
|
+
// inline element of zero size, and dropping the margin keeps it from occupying layout.
|
|
21035
|
+
'[class.d-block]': 'activeSignal()',
|
|
21036
|
+
'[class.dbx-mb3]': 'activeSignal()'
|
|
21037
|
+
},
|
|
21038
|
+
imports: [MatIconModule, DbxFileUploadComponent],
|
|
21039
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
21040
|
+
standalone: true
|
|
21041
|
+
}]
|
|
21042
|
+
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], expectedSlotIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expectedSlotIds", required: false }] }], enforceExpectedSlots: [{ type: i0.Input, args: [{ isSignal: true, alias: "enforceExpectedSlots", required: false }] }], imported: [{ type: i0.Output, args: ["imported"] }], importFailed: [{ type: i0.Output, args: ["importFailed"] }], missingSlots: [{ type: i0.Output, args: ["missingSlots"] }] } });
|
|
21043
|
+
|
|
19547
21044
|
const DEFAULT_UPLOAD_FOOTER_ICON = 'cloud_upload';
|
|
19548
21045
|
const DEFAULT_UPLOAD_FOOTER_TEXT = 'Upload';
|
|
19549
21046
|
const DEFAULT_UPLOAD_FOOTER_COLOR = 'primary';
|
|
@@ -19605,7 +21102,7 @@ class DbxPdfMergeUploadDialogComponent extends AbstractDialogDirective {
|
|
|
19605
21102
|
</div>
|
|
19606
21103
|
}
|
|
19607
21104
|
</dbx-dialog-content>
|
|
19608
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: DbxDialogContentDirective, selector: "dbx-dialog-content,[dbxDialogContent],.dbx-dialog-content", inputs: ["width"] }, { kind: "component", type: DbxDialogContentCloseComponent, selector: "dbx-dialog-content-close", inputs: ["padded"], outputs: ["close"] }, { kind: "component", type: DbxInjectionComponent, selector: "dbx-injection, [dbxInjection], [dbx-injection]", inputs: ["config", "template"] }, { kind: "component", type: DbxPdfMergeEditorComponent, selector: "dbx-pdf-merge-editor", inputs: ["accept", "multiple", "fileName", "showDownloadButton", "showPreviewButton", "downloadButton", "showAddFiles", "showFileList", "config"], outputs: ["entriesChanged"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "customContent", "allowClickPropagation", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
21105
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: DbxDialogContentDirective, selector: "dbx-dialog-content,[dbxDialogContent],.dbx-dialog-content", inputs: ["width"] }, { kind: "component", type: DbxDialogContentCloseComponent, selector: "dbx-dialog-content-close", inputs: ["padded"], outputs: ["close"] }, { kind: "component", type: DbxInjectionComponent, selector: "dbx-injection, [dbxInjection], [dbx-injection]", inputs: ["config", "template"] }, { kind: "component", type: DbxPdfMergeEditorComponent, selector: "dbx-pdf-merge-editor", inputs: ["accept", "multiple", "fileName", "showDownloadButton", "showPreviewButton", "downloadButton", "showAddFiles", "showFileList", "pageEditing", "sidecar", "config"], outputs: ["entriesChanged"] }, { kind: "component", type: DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "customContent", "allowClickPropagation", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19609
21106
|
}
|
|
19610
21107
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DbxPdfMergeUploadDialogComponent, decorators: [{
|
|
19611
21108
|
type: Component,
|
|
@@ -19808,5 +21305,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImpo
|
|
|
19808
21305
|
* Generated bundle index. Do not edit.
|
|
19809
21306
|
*/
|
|
19810
21307
|
|
|
19811
|
-
export { APP_POPUP_MINIMIZED_WIDTH, APP_POPUP_NORMAL_HEIGHT, APP_POPUP_NORMAL_WIDTH, AbstractDbxClipboardDirective, AbstractDbxErrorWidgetComponent, AbstractDbxFileUploadComponent, AbstractDbxHelpWidgetDirective, AbstractDbxListAccordionViewDirective, AbstractDbxListGridViewDirective, AbstractDbxListViewDirective, AbstractDbxListWrapperDirective, AbstractDbxPartialPresetFilterMenuDirective, AbstractDbxSegueAnchorDirective, AbstractDbxSelectionListViewDirective, AbstractDbxSelectionListWrapperDirective, AbstractDbxValueListItemModifierDirective, AbstractDbxValueListViewDirective, AbstractDbxValueListViewItemComponent, AbstractDbxWidgetComponent, AbstractDialogDirective, AbstractFilterPopoverButtonDirective, AbstractPopoverDirective, AbstractPopoverRefDirective, AbstractPopoverRefWithEventsDirective, AbstractPopupDirective, AbstractPromptConfirmDirective, CompactContextStore, CompactMode, DBX_ACTION_SNACKBAR_DEFAULTS, DBX_ACTION_SNACKBAR_SERVICE_CONFIG, DBX_AVATAR_CONTEXT_DATA_TOKEN, DBX_COLOR_CUSTOM_BG_CSS_CLASS, DBX_COLOR_CUSTOM_TEXT_CSS_CLASS, DBX_CURATED_COLOR_COUNT, DBX_CURATED_COLOR_TEMPLATES, DBX_CURATED_COLOR_TEMPLATE_KEY_PREFIX, DBX_DARK_STYLE_CLASS_SUFFIX, DBX_HELP_WIDGET_ENTRY_DATA_TOKEN, DBX_LIST_ACCORDION_VIEW_COMPONENT_IMPORTS_AND_EXPORTS, DBX_LIST_GRID_VIEW_COMPONENT_IMPORTS_AND_EXPORTS, DBX_LIST_ITEM_DISABLE_RIPPLE_LIST_ITEM_MODIFIER_KEY, DBX_LIST_ITEM_IS_SELECTED_ITEM_MODIFIER_KEY, DBX_MODEL_VIEW_TRACKER_STORAGE_ACCESSOR_TOKEN, DBX_PDF_MERGE_EDITOR_CONFIG, DBX_PDF_MERGE_EDITOR_INITIAL_STATE, DBX_PDF_MERGE_EDITOR_PRESERVE_ENTRIES_ON_SLOT_DESTROY, DBX_PDF_MERGE_ENCRYPTED_ERROR_MESSAGE, DBX_PROGRESS_BUTTON_GLOBAL_CONFIG, DBX_ROUTER_ANCHOR_COMPONENTS, DBX_ROUTER_VALUE_LIST_ITEM_MODIFIER_KEY, DBX_THEME_COLORS, DBX_THEME_COLORS_EXTRA, DBX_THEME_COLORS_EXTRA_SECONDARY, DBX_THEME_COLORS_MAIN, DBX_VALUE_LIST_VIEW_ITEM, DBX_WEB_FILE_PREVIEW_SERVICE_ENTRIES_TOKEN, DBX_WEB_FILE_PREVIEW_SERVICE_ZIP_COMPONENT_PRESET, DBX_WEB_FILE_PREVIEW_SERVICE_ZIP_PRESET_ENTRY, DBX_WEB_PAGE_TITLE_SERVICE_CONFIG, DEFAULT_DBX_CHIP_TONE, DEFAULT_DBX_DETACH_KEY, DEFAULT_DBX_ERROR_SNACKBAR_CONFIG, DEFAULT_DBX_HELP_VIEW_POPOVER_KEY, DEFAULT_DBX_LINKIFY_STRING_TYPE, DEFAULT_DBX_LIST_ACCORDION_VIEW_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_LIST_GRID_VIEW_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_LIST_ITEM_DISABLE_FUNCTION, DEFAULT_DBX_LIST_ITEM_IS_SELECTED_FUNCTION, DEFAULT_DBX_LIST_SCROLL_DISTANCE, DEFAULT_DBX_LIST_THROTTLE_SCROLL, DEFAULT_DBX_LIST_VIEW_META_ICON, DEFAULT_DBX_PDF_MERGE_ENCRYPTED_HANDLING, DEFAULT_DBX_PROMPT_CONFIRM_DIALOG_CONFIG, DEFAULT_DBX_SELECTION_VALUE_LIST_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_SIDENAV_MENU_ICON, DEFAULT_DBX_STYLE_CONFIG_TOKEN, DEFAULT_DBX_VALUE_LIST_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_WEB_FILE_PREVIEW_SERVICE_DIALOG_WITH_COMPONENT_FUNCTION, DEFAULT_DBX_WEB_FILE_PREVIEW_SERVICE_PREVIEW_COMPONENT_FUNCTION, DEFAULT_ERROR_POPOVER_KEY, DEFAULT_ERROR_WIDGET_CODE, DEFAULT_FILTER_POPOVER_KEY, DEFAULT_IMAGE_BITMAP_TO_BLOB_ENCODER, DEFAULT_IMAGE_JPEG_QUALITY, DEFAULT_LIST_GRID_SIZE_CONFIG, DEFAULT_LIST_WRAPPER_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_LOADING_PROGRESS_DIAMETER, DEFAULT_PDF_MERGE_ACCEPT, DEFAULT_SCREEN_MEDIA_SERVICE_CONFIG, DEFAULT_SNACKBAR_DIRECTIVE_DURATION, DEFAULT_TWO_COLUMNS_MIN_RIGHT_WIDTH, DEFAULT_VALUE_LIST_VIEW_CONTENT_COMPONENT_TRACK_BY_FUNCTION, DbxAccordionHeaderHeightDirective, DbxActionConfirmDirective, DbxActionDialogDirective, DbxActionErrorDirective, DbxActionKeyTriggerDirective, DbxActionLoadingContextDirective, DbxActionModule, DbxActionPopoverDirective, DbxActionSnackbarComponent, DbxActionSnackbarDirective, DbxActionSnackbarErrorDirective, DbxActionSnackbarModule, DbxActionSnackbarService, DbxActionTransitionSafetyDirective, DbxActionUIRouterTransitionSafetyDialogComponent, DbxAnchorComponent, DbxAnchorContentComponent, DbxAnchorIconComponent, DbxAnchorListComponent, DbxAngularRouterSegueAnchorComponent, DbxAvatarComponent, DbxAvatarViewComponent, DbxAvatarViewService, DbxAvatarViewServiceConfig, DbxBarDirective, DbxBarHeaderComponent, DbxBarLayoutModule, DbxBasicLoadingComponent, DbxBodyDirective, DbxButtonComponent, DbxButtonModule, DbxButtonSpacerDirective, DbxCardBoxComponent, DbxCardBoxContainerDirective, DbxCardBoxLayoutModule, DbxChipDirective, DbxChipListComponent, DbxClickToCopyTextComponent, DbxClickToCopyTextDirective, DbxColorDirective, DbxColorService, DbxColorServiceConfig, DbxColumnLayoutModule, DbxCompactDirective, DbxCompactLayoutModule, DbxContentBorderDirective, DbxContentBoxDirective, DbxContentContainerDirective, DbxContentDirective, DbxContentElevateDirective, DbxContentLayoutModule, DbxContentPageDirective, DbxContentPitDirective, DbxDetachContentComponent, DbxDetachControlButtonsComponent, DbxDetachController, DbxDetachControlsComponent, DbxDetachInitDirective, DbxDetachInteractionModule, DbxDetachOutletComponent, DbxDetachOverlayComponent, DbxDetachService, DbxDetachWindowState, DbxDetailBlockComponent, DbxDetailBlockHeaderComponent, DbxDialogContentCloseComponent, DbxDialogContentDirective, DbxDialogContentFooterComponent, DbxDialogInteractionModule, DbxDialogModule, DbxDownloadBlobButtonComponent, DbxDownloadTextViewComponent, DbxEmbedComponent, DbxErrorComponent, DbxErrorDefaultErrorWidgetComponent, DbxErrorDetailsComponent, DbxErrorPopoverComponent, DbxErrorSnackbarComponent, DbxErrorSnackbarService, DbxErrorViewComponent, DbxErrorWidgetService, DbxErrorWidgetViewComponent, DbxFileUploadActionCompatable, DbxFileUploadActionSyncDirective, DbxFileUploadAreaComponent, DbxFileUploadButtonComponent, DbxFileUploadComponent, DbxFilterInteractionModule, DbxFilterPopoverButtonComponent, DbxFilterPopoverComponent, DbxFilterWrapperComponent, DbxFlagComponent, DbxFlagLayoutModule, DbxFlagPromptComponent, DbxFlexGroupDirective, DbxFlexLayoutModule, DbxFlexSizeDirective, DbxHelpContextDirective, DbxHelpContextService, DbxHelpViewListComponent, DbxHelpViewListEntryComponent, DbxHelpViewPopoverButtonComponent, DbxHelpViewPopoverComponent, DbxHelpWidgetService, DbxHelpWidgetServiceConfig, DbxIconButtonComponent, DbxIconButtonModule, DbxIconItemComponent, DbxIconSpacerDirective, DbxIconTileComponent, DbxIconTileDirective, DbxIfSidenavDisplayModeDirective, DbxIframeComponent, DbxInjectionDialogComponent, DbxInteractionModule, DbxIntroActionSectionComponent, DbxLabelBlockComponent, DbxLayoutModule, DbxLinkComponent, DbxLinkifyComponent, DbxLinkifyService, DbxLinkifyServiceConfig, DbxListAccordionViewComponentImportsModule, DbxListComponent, DbxListEmptyContentComponent, DbxListGridViewComponentImportsModule, DbxListInternalContentDirective, DbxListItemAnchorModifierDirective, DbxListItemDisableRippleModifierDirective, DbxListItemIsSelectedModifierDirective, DbxListModifierModule, DbxListModule, DbxListTitleGroupDirective, DbxListView, DbxListViewMetaIconComponent, DbxListViewWrapper, DbxListWrapperComponentImportsModule, DbxLoadingComponent, DbxLoadingErrorDirective, DbxLoadingModule, DbxLoadingProgressComponent, DbxModelObjectStateService, actions as DbxModelStateActions, model_actions as DbxModelStateModelActions, DbxModelTrackerService, DbxModelTypesService, DbxModelViewTrackerStorage, DbxNavbarComponent, DbxNumberWithLimitComponent, DbxOneColumnComponent, DbxOneColumnLayoutModule, DbxPagebarComponent, DbxPartialPresetFilterListComponent, DbxPartialPresetFilterMenuComponent, DbxPdfMergeEditorComponent, DbxPdfMergeEditorFileUploadComponent, DbxPdfMergeEditorFileUploadHasStateDirective, DbxPdfMergeEditorFileUploadValidatorDirective, DbxPdfMergeEditorStore, DbxPdfMergeEditorStoreDirective, DbxPdfMergeEntryComponent, DbxPdfMergeListComponent, DbxPdfMergeUploadActionDirective, DbxPdfMergeUploadButtonDirective, DbxPdfMergeUploadDialogComponent, DbxPdfPreviewComponent, DbxPopoverCloseButtonComponent, DbxPopoverComponent, DbxPopoverComponentController, DbxPopoverContentComponent, DbxPopoverController, DbxPopoverControlsDirective, DbxPopoverCoordinatorComponent, DbxPopoverCoordinatorService, DbxPopoverHeaderComponent, DbxPopoverInteractionContentModule, DbxPopoverInteractionModule, DbxPopoverScrollContentDirective, DbxPopoverService, DbxPopupComponent, DbxPopupComponentController, DbxPopupContentComponent, DbxPopupControlButtonsComponent, DbxPopupController, DbxPopupControlsComponent, DbxPopupCoordinatorComponent, DbxPopupCoordinatorService, DbxPopupInteractionModule, DbxPopupService, DbxPopupWindowState, DbxPresetFilterListComponent, DbxPresetFilterMenuComponent, DbxProgressBarButtonComponent, DbxProgressButtonsModule, DbxProgressSpinnerButtonComponent, DbxPromptBoxDirective, DbxPromptComponent, DbxPromptConfirm, DbxPromptConfirmButtonDirective, DbxPromptConfirmComponent, DbxPromptConfirmDialogComponent, DbxPromptConfirmDirective, DbxPromptModule, DbxPromptPageComponent, DbxReadableErrorModule, DbxResizedDirective, DbxRouterAnchorModule, DbxRouterLayoutModule, DbxRouterSidenavModule, DbxRouterWebProviderConfig, DbxScreenMediaService, DbxScreenMediaServiceConfig, DbxSectionComponent, DbxSectionHeaderComponent, DbxSectionLayoutModule, DbxSectionPageComponent, DbxSelectionValueListViewComponent, DbxSelectionValueListViewComponentImportsModule, DbxSelectionValueListViewContentComponent, DbxSetStyleDirective, DbxSidenavButtonComponent, DbxSidenavComponent, DbxSidenavPageComponent, DbxSidenavPagebarComponent, DbxSpacerDirective, DbxStepBlockComponent, DbxStructureDirective, DbxStructureModule, DbxStyleBodyDirective, DbxStyleDirective, DbxStyleLayoutModule, DbxStyleService, DbxSubSectionComponent, DbxTextChipsComponent, DbxTextColorDirective, DbxTextModule, DbxTwoBlockComponent, DbxTwoColumnBackDirective, DbxTwoColumnColumnHeadDirective, DbxTwoColumnComponent, DbxTwoColumnContextDirective, DbxTwoColumnFullLeftDirective, DbxTwoColumnLayoutModule, DbxTwoColumnRightComponent, DbxTwoColumnSrefDirective, DbxTwoColumnSrefShowRightDirective, DbxUIRouterSegueAnchorComponent, DbxUnitedStatesAddressComponent, DbxValueListAccordionViewComponent, DbxValueListAccordionViewContentComponent, DbxValueListAccordionViewContentGroupComponent, DbxValueListGridSizeDirective, DbxValueListGridViewComponent, DbxValueListGridViewContentComponent, DbxValueListGridViewContentGroupComponent, DbxValueListItemModifier, DbxValueListItemModifierDirective, DbxValueListView, DbxValueListViewComponent, DbxValueListViewComponentImportsModule, DbxValueListViewContentComponent, DbxValueListViewContentGroupComponent, DbxValueListViewGroupDelegate, DbxWebFilePreviewComponent, DbxWebFilePreviewService, DbxWebModule, DbxWebPageTitleInfoDirective, DbxWebPageTitleService, DbxWidgetListGridComponent, DbxWidgetListGridViewComponent, DbxWidgetListGridViewItemComponent, DbxWidgetService, DbxWidgetViewComponent, DbxWindowKeyDownListenerDirective, DbxZipBlobPreviewComponent, DbxZipPreviewComponent, PDF_MERGE_RESULT_MIME_TYPE, PopoverPositionStrategy, PopupGlobalPositionStrategy, SCREEN_MEDIA_WIDTH_TYPE_SIZE_MAP, SIDE_NAV_DISPLAY_MODE_ORDER, SideNavDisplayMode, TRACK_BY_MODEL_ID, TRACK_BY_MODEL_KEY, TwoColumnsContextStore, UNKNOWN_ERROR_WIDGET_CODE, addConfigToValueListItems, allDbxModelViewTrackerEventModelKeys, allDbxModelViewTrackerEventSetModelKeys, buildPdfMergeEntry, buildPdfMergeEntrySync, catchErrorServerParams, classifyPdfMergeFile, compactModeFromInput, compareScreenMediaWidthTypes, compressImageFile, convertServerErrorParams, convertToPOJOServerErrorResponse, convertToServerErrorResponse, copyToClipboardFunction, dbxColorBackground, dbxCuratedColorConfigForString, dbxListAccordionViewComponentImportsAndExports, dbxListGridViewComponentImportsAndExports, dbxPresetFilterMenuButtonIconObservable, dbxPresetFilterMenuButtonTextObservable, dbxStyleClassCleanSuffix, dbxThemeColorCssToken, dbxThemeColorCssTokenVar, dbxThemeColorCssVariable, dbxThemeColorCssVariableVar, dbxValueListItemDecisionFunction, dbxValueListItemKeyForItemValue, dbxWebDefaultPageTitleDelegate, dbxZipBlobPreviewEntryTreeFromEntries, defaultDbxModelViewTrackerStorageAccessorFactory, defaultDbxValueListViewGroupDelegate, defaultDbxValueListViewGroupValuesFunction, disableRightClickInCdkBackdrop, fileAcceptFilterTypeStringArray, fileAcceptFunction, fileAcceptString, fileArrayAcceptMatchFunction, flattenAccordionGroups, formatPdfMergeEntrySize, index as fromDbxModel, injectCopyToClipboardFunction, injectCopyToClipboardFunctionWithSnackbarMessage, isDbxColorConfig, listItemModifier, makeDbxActionSnackbarDisplayConfigGeneratorFunction, mapCompactModeObs, mapValuesToValuesListItemConfigObs, mergePdfMergeEntries, index$1 as onDbxModel, openEmbedDialog, openIframeDialog, openPdfPreviewDialog, openZipPreviewDialog, overrideClickElementEffect, provideDbxDetachController, provideDbxFileUploadActionCompatable, provideDbxHelpServices, provideDbxLinkify, provideDbxListView, provideDbxListViewWrapper, provideDbxModelService, provideDbxPdfMergeEditorConfig, provideDbxPdfMergeEditorPreserveEntriesOnSlotDestroy, provideDbxProgressButtonGlobalConfig, provideDbxPromptConfirm, provideDbxRouterWebAngularRouterProviderConfig, provideDbxRouterWebUiRouterProviderConfig, provideDbxScreenMediaService, provideDbxStyleService, provideDbxValueListView, provideDbxValueListViewGroupDelegate, provideDbxValueListViewModifier, provideDbxWebFilePreviewServiceEntries, provideDbxWebPageTitleService, provideTwoColumnsContext, registerHelpContextKeysWithDbxHelpContextService, resizeSignal, resolveSideNavDisplayMode, sanitizeDbxDialogContentConfig, screenMediaWidthTypeIsActive, trackByModelKeyRef, trackByUniqueIdentifier, validatePdfMergeEntry };
|
|
21308
|
+
export { APP_POPUP_MINIMIZED_WIDTH, APP_POPUP_NORMAL_HEIGHT, APP_POPUP_NORMAL_WIDTH, AbstractDbxClipboardDirective, AbstractDbxErrorWidgetComponent, AbstractDbxFileUploadComponent, AbstractDbxHelpWidgetDirective, AbstractDbxListAccordionViewDirective, AbstractDbxListGridViewDirective, AbstractDbxListViewDirective, AbstractDbxListWrapperDirective, AbstractDbxPartialPresetFilterMenuDirective, AbstractDbxSegueAnchorDirective, AbstractDbxSelectionListViewDirective, AbstractDbxSelectionListWrapperDirective, AbstractDbxValueListItemModifierDirective, AbstractDbxValueListViewDirective, AbstractDbxValueListViewItemComponent, AbstractDbxWidgetComponent, AbstractDialogDirective, AbstractFilterPopoverButtonDirective, AbstractPopoverDirective, AbstractPopoverRefDirective, AbstractPopoverRefWithEventsDirective, AbstractPopupDirective, AbstractPromptConfirmDirective, CompactContextStore, CompactMode, DBX_ACTION_SNACKBAR_DEFAULTS, DBX_ACTION_SNACKBAR_SERVICE_CONFIG, DBX_AVATAR_CONTEXT_DATA_TOKEN, DBX_COLOR_CUSTOM_BG_CSS_CLASS, DBX_COLOR_CUSTOM_TEXT_CSS_CLASS, DBX_CURATED_COLOR_COUNT, DBX_CURATED_COLOR_TEMPLATES, DBX_CURATED_COLOR_TEMPLATE_KEY_PREFIX, DBX_DARK_STYLE_CLASS_SUFFIX, DBX_HELP_WIDGET_ENTRY_DATA_TOKEN, DBX_LIST_ACCORDION_VIEW_COMPONENT_IMPORTS_AND_EXPORTS, DBX_LIST_GRID_VIEW_COMPONENT_IMPORTS_AND_EXPORTS, DBX_LIST_ITEM_DISABLE_RIPPLE_LIST_ITEM_MODIFIER_KEY, DBX_LIST_ITEM_IS_SELECTED_ITEM_MODIFIER_KEY, DBX_MODEL_VIEW_TRACKER_STORAGE_ACCESSOR_TOKEN, DBX_PDF_MERGE_EDITOR_CONFIG, DBX_PDF_MERGE_EDITOR_INITIAL_STATE, DBX_PDF_MERGE_EDITOR_PRESERVE_ENTRIES_ON_SLOT_DESTROY, DBX_PDF_MERGE_ENCRYPTED_ERROR_MESSAGE, DBX_PDF_MERGE_ENCRYPTED_NOT_EDITABLE_MESSAGE, DBX_PDF_MERGE_IMPORT_ERROR_MESSAGES, DBX_PROGRESS_BUTTON_GLOBAL_CONFIG, DBX_ROUTER_ANCHOR_COMPONENTS, DBX_ROUTER_VALUE_LIST_ITEM_MODIFIER_KEY, DBX_THEME_COLORS, DBX_THEME_COLORS_EXTRA, DBX_THEME_COLORS_EXTRA_SECONDARY, DBX_THEME_COLORS_MAIN, DBX_VALUE_LIST_VIEW_ITEM, DBX_WEB_FILE_PREVIEW_SERVICE_ENTRIES_TOKEN, DBX_WEB_FILE_PREVIEW_SERVICE_ZIP_COMPONENT_PRESET, DBX_WEB_FILE_PREVIEW_SERVICE_ZIP_PRESET_ENTRY, DBX_WEB_PAGE_TITLE_SERVICE_CONFIG, DEFAULT_DBX_CHIP_TONE, DEFAULT_DBX_DETACH_KEY, DEFAULT_DBX_ERROR_SNACKBAR_CONFIG, DEFAULT_DBX_HELP_VIEW_POPOVER_KEY, DEFAULT_DBX_LINKIFY_STRING_TYPE, DEFAULT_DBX_LIST_ACCORDION_VIEW_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_LIST_GRID_VIEW_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_LIST_ITEM_DISABLE_FUNCTION, DEFAULT_DBX_LIST_ITEM_IS_SELECTED_FUNCTION, DEFAULT_DBX_LIST_SCROLL_DISTANCE, DEFAULT_DBX_LIST_THROTTLE_SCROLL, DEFAULT_DBX_LIST_VIEW_META_ICON, DEFAULT_DBX_PDF_MERGE_ENCRYPTED_HANDLING, DEFAULT_DBX_PDF_MERGE_PAGE_EDITING, DEFAULT_DBX_PDF_MERGE_SIDECAR, DEFAULT_DBX_PROMPT_CONFIRM_DIALOG_CONFIG, DEFAULT_DBX_SELECTION_VALUE_LIST_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_SIDENAV_MENU_ICON, DEFAULT_DBX_STYLE_CONFIG_TOKEN, DEFAULT_DBX_VALUE_LIST_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_DBX_WEB_FILE_PREVIEW_SERVICE_DIALOG_WITH_COMPONENT_FUNCTION, DEFAULT_DBX_WEB_FILE_PREVIEW_SERVICE_PREVIEW_COMPONENT_FUNCTION, DEFAULT_ERROR_POPOVER_KEY, DEFAULT_ERROR_WIDGET_CODE, DEFAULT_FILTER_POPOVER_KEY, DEFAULT_IMAGE_BITMAP_TO_BLOB_ENCODER, DEFAULT_IMAGE_JPEG_QUALITY, DEFAULT_LIST_GRID_SIZE_CONFIG, DEFAULT_LIST_WRAPPER_COMPONENT_CONFIGURATION_TEMPLATE, DEFAULT_LOADING_PROGRESS_DIAMETER, DEFAULT_PDF_MERGE_ACCEPT, DEFAULT_PDF_MERGE_PAGE_GROUP_KEY, DEFAULT_SCREEN_MEDIA_SERVICE_CONFIG, DEFAULT_SNACKBAR_DIRECTIVE_DURATION, DEFAULT_TWO_COLUMNS_MIN_RIGHT_WIDTH, DEFAULT_VALUE_LIST_VIEW_CONTENT_COMPONENT_TRACK_BY_FUNCTION, DbxAccordionHeaderHeightDirective, DbxActionConfirmDirective, DbxActionDialogDirective, DbxActionErrorDirective, DbxActionKeyTriggerDirective, DbxActionLoadingContextDirective, DbxActionModule, DbxActionPopoverDirective, DbxActionSnackbarComponent, DbxActionSnackbarDirective, DbxActionSnackbarErrorDirective, DbxActionSnackbarModule, DbxActionSnackbarService, DbxActionTransitionSafetyDirective, DbxActionUIRouterTransitionSafetyDialogComponent, DbxAnchorComponent, DbxAnchorContentComponent, DbxAnchorIconComponent, DbxAnchorListComponent, DbxAngularRouterSegueAnchorComponent, DbxAvatarComponent, DbxAvatarViewComponent, DbxAvatarViewService, DbxAvatarViewServiceConfig, DbxBarDirective, DbxBarHeaderComponent, DbxBarLayoutModule, DbxBasicLoadingComponent, DbxBodyDirective, DbxButtonComponent, DbxButtonModule, DbxButtonSpacerDirective, DbxCardBoxComponent, DbxCardBoxContainerDirective, DbxCardBoxLayoutModule, DbxChipDirective, DbxChipListComponent, DbxClickToCopyTextComponent, DbxClickToCopyTextDirective, DbxColorDirective, DbxColorService, DbxColorServiceConfig, DbxColumnLayoutModule, DbxCompactDirective, DbxCompactLayoutModule, DbxContentBorderDirective, DbxContentBoxDirective, DbxContentContainerDirective, DbxContentDirective, DbxContentElevateDirective, DbxContentLayoutModule, DbxContentPageDirective, DbxContentPitDirective, DbxDetachContentComponent, DbxDetachControlButtonsComponent, DbxDetachController, DbxDetachControlsComponent, DbxDetachInitDirective, DbxDetachInteractionModule, DbxDetachOutletComponent, DbxDetachOverlayComponent, DbxDetachService, DbxDetachWindowState, DbxDetailBlockComponent, DbxDetailBlockHeaderComponent, DbxDialogContentCloseComponent, DbxDialogContentDirective, DbxDialogContentFooterComponent, DbxDialogInteractionModule, DbxDialogModule, DbxDownloadBlobButtonComponent, DbxDownloadTextViewComponent, DbxEmbedComponent, DbxErrorComponent, DbxErrorDefaultErrorWidgetComponent, DbxErrorDetailsComponent, DbxErrorPopoverComponent, DbxErrorSnackbarComponent, DbxErrorSnackbarService, DbxErrorViewComponent, DbxErrorWidgetService, DbxErrorWidgetViewComponent, DbxFileUploadActionCompatable, DbxFileUploadActionSyncDirective, DbxFileUploadAreaComponent, DbxFileUploadButtonComponent, DbxFileUploadComponent, DbxFilterInteractionModule, DbxFilterPopoverButtonComponent, DbxFilterPopoverComponent, DbxFilterWrapperComponent, DbxFlagComponent, DbxFlagLayoutModule, DbxFlagPromptComponent, DbxFlexGroupDirective, DbxFlexLayoutModule, DbxFlexSizeDirective, DbxHelpContextDirective, DbxHelpContextService, DbxHelpViewListComponent, DbxHelpViewListEntryComponent, DbxHelpViewPopoverButtonComponent, DbxHelpViewPopoverComponent, DbxHelpWidgetService, DbxHelpWidgetServiceConfig, DbxIconButtonComponent, DbxIconButtonModule, DbxIconItemComponent, DbxIconSpacerDirective, DbxIconTileComponent, DbxIconTileDirective, DbxIfSidenavDisplayModeDirective, DbxIframeComponent, DbxInjectionDialogComponent, DbxInteractionModule, DbxIntroActionSectionComponent, DbxLabelBlockComponent, DbxLayoutModule, DbxLinkComponent, DbxLinkifyComponent, DbxLinkifyService, DbxLinkifyServiceConfig, DbxListAccordionViewComponentImportsModule, DbxListComponent, DbxListEmptyContentComponent, DbxListGridViewComponentImportsModule, DbxListInternalContentDirective, DbxListItemAnchorModifierDirective, DbxListItemDisableRippleModifierDirective, DbxListItemIsSelectedModifierDirective, DbxListModifierModule, DbxListModule, DbxListTitleGroupDirective, DbxListView, DbxListViewMetaIconComponent, DbxListViewWrapper, DbxListWrapperComponentImportsModule, DbxLoadingComponent, DbxLoadingErrorDirective, DbxLoadingModule, DbxLoadingProgressComponent, DbxModelObjectStateService, actions as DbxModelStateActions, model_actions as DbxModelStateModelActions, DbxModelTrackerService, DbxModelTypesService, DbxModelViewTrackerStorage, DbxNavbarComponent, DbxNumberWithLimitComponent, DbxOneColumnComponent, DbxOneColumnLayoutModule, DbxPagebarComponent, DbxPartialPresetFilterListComponent, DbxPartialPresetFilterMenuComponent, DbxPdfMergeEditorComponent, DbxPdfMergeEditorFileUploadComponent, DbxPdfMergeEditorFileUploadHasStateDirective, DbxPdfMergeEditorFileUploadValidatorDirective, DbxPdfMergeEditorStore, DbxPdfMergeEditorStoreDirective, DbxPdfMergeEntryComponent, DbxPdfMergeImportComponent, DbxPdfMergeListComponent, DbxPdfMergePageComponent, DbxPdfMergePageListComponent, DbxPdfMergeUploadActionDirective, DbxPdfMergeUploadButtonDirective, DbxPdfMergeUploadDialogComponent, DbxPdfPreviewComponent, DbxPopoverCloseButtonComponent, DbxPopoverComponent, DbxPopoverComponentController, DbxPopoverContentComponent, DbxPopoverController, DbxPopoverControlsDirective, DbxPopoverCoordinatorComponent, DbxPopoverCoordinatorService, DbxPopoverHeaderComponent, DbxPopoverInteractionContentModule, DbxPopoverInteractionModule, DbxPopoverScrollContentDirective, DbxPopoverService, DbxPopupComponent, DbxPopupComponentController, DbxPopupContentComponent, DbxPopupControlButtonsComponent, DbxPopupController, DbxPopupControlsComponent, DbxPopupCoordinatorComponent, DbxPopupCoordinatorService, DbxPopupInteractionModule, DbxPopupService, DbxPopupWindowState, DbxPresetFilterListComponent, DbxPresetFilterMenuComponent, DbxProgressBarButtonComponent, DbxProgressButtonsModule, DbxProgressSpinnerButtonComponent, DbxPromptBoxDirective, DbxPromptComponent, DbxPromptConfirm, DbxPromptConfirmButtonDirective, DbxPromptConfirmComponent, DbxPromptConfirmDialogComponent, DbxPromptConfirmDirective, DbxPromptModule, DbxPromptPageComponent, DbxReadableErrorModule, DbxResizedDirective, DbxRouterAnchorModule, DbxRouterLayoutModule, DbxRouterSidenavModule, DbxRouterWebProviderConfig, DbxScreenMediaService, DbxScreenMediaServiceConfig, DbxSectionComponent, DbxSectionHeaderComponent, DbxSectionLayoutModule, DbxSectionPageComponent, DbxSelectionValueListViewComponent, DbxSelectionValueListViewComponentImportsModule, DbxSelectionValueListViewContentComponent, DbxSetStyleDirective, DbxSidenavButtonComponent, DbxSidenavComponent, DbxSidenavPageComponent, DbxSidenavPagebarComponent, DbxSpacerDirective, DbxStepBlockComponent, DbxStructureDirective, DbxStructureModule, DbxStyleBodyDirective, DbxStyleDirective, DbxStyleLayoutModule, DbxStyleService, DbxSubSectionComponent, DbxTextChipsComponent, DbxTextColorDirective, DbxTextModule, DbxTwoBlockComponent, DbxTwoColumnBackDirective, DbxTwoColumnColumnHeadDirective, DbxTwoColumnComponent, DbxTwoColumnContextDirective, DbxTwoColumnFullLeftDirective, DbxTwoColumnLayoutModule, DbxTwoColumnRightComponent, DbxTwoColumnSrefDirective, DbxTwoColumnSrefShowRightDirective, DbxUIRouterSegueAnchorComponent, DbxUnitedStatesAddressComponent, DbxValueListAccordionViewComponent, DbxValueListAccordionViewContentComponent, DbxValueListAccordionViewContentGroupComponent, DbxValueListGridSizeDirective, DbxValueListGridViewComponent, DbxValueListGridViewContentComponent, DbxValueListGridViewContentGroupComponent, DbxValueListItemModifier, DbxValueListItemModifierDirective, DbxValueListView, DbxValueListViewComponent, DbxValueListViewComponentImportsModule, DbxValueListViewContentComponent, DbxValueListViewContentGroupComponent, DbxValueListViewGroupDelegate, DbxWebFilePreviewComponent, DbxWebFilePreviewService, DbxWebModule, DbxWebPageTitleInfoDirective, DbxWebPageTitleService, DbxWidgetListGridComponent, DbxWidgetListGridViewComponent, DbxWidgetListGridViewItemComponent, DbxWidgetService, DbxWidgetViewComponent, DbxWindowKeyDownListenerDirective, DbxZipBlobPreviewComponent, DbxZipPreviewComponent, PDF_MERGE_PAGE_ID_SEPARATOR, PDF_MERGE_PAGE_TAG_KEY, PDF_MERGE_RESULT_MIME_TYPE, PDF_MERGE_SIDECAR_DESCRIPTION, PDF_MERGE_SIDECAR_FILE_NAME, PDF_MERGE_SIDECAR_VERSION, PopoverPositionStrategy, PopupGlobalPositionStrategy, SCREEN_MEDIA_WIDTH_TYPE_SIZE_MAP, SIDE_NAV_DISPLAY_MODE_ORDER, SideNavDisplayMode, TRACK_BY_MODEL_ID, TRACK_BY_MODEL_KEY, TwoColumnsContextStore, UNKNOWN_ERROR_WIDGET_CODE, addConfigToValueListItems, allDbxModelViewTrackerEventModelKeys, allDbxModelViewTrackerEventSetModelKeys, asPdfMergeFile, attachPdfMergeSidecar, buildPdfMergeEntriesFromSidecar, buildPdfMergeEntry, buildPdfMergeEntrySync, buildPdfMergePagePlan, catchErrorServerParams, classifyPdfMergeFile, compactModeFromInput, compareScreenMediaWidthTypes, compressImageFile, convertServerErrorParams, convertToPOJOServerErrorResponse, convertToServerErrorResponse, copyToClipboardFunction, dbxColorBackground, dbxCuratedColorConfigForString, dbxListAccordionViewComponentImportsAndExports, dbxListGridViewComponentImportsAndExports, dbxPresetFilterMenuButtonIconObservable, dbxPresetFilterMenuButtonTextObservable, dbxStyleClassCleanSuffix, dbxThemeColorCssToken, dbxThemeColorCssTokenVar, dbxThemeColorCssVariable, dbxThemeColorCssVariableVar, dbxValueListItemDecisionFunction, dbxValueListItemKeyForItemValue, dbxWebDefaultPageTitleDelegate, dbxZipBlobPreviewEntryTreeFromEntries, defaultDbxModelViewTrackerStorageAccessorFactory, defaultDbxValueListViewGroupDelegate, defaultDbxValueListViewGroupValuesFunction, disableRightClickInCdkBackdrop, entryIdForPdfMergePageId, fileAcceptFilterTypeStringArray, fileAcceptFunction, fileAcceptString, fileArrayAcceptMatchFunction, flattenAccordionGroups, formatPdfMergeEntrySize, index as fromDbxModel, injectCopyToClipboardFunction, injectCopyToClipboardFunctionWithSnackbarMessage, isDbxColorConfig, listItemModifier, makeDbxActionSnackbarDisplayConfigGeneratorFunction, makePdfMergePageId, makePdfMergeSidecar, makePdfMergeSidecarPageTag, mapCompactModeObs, mapValuesToValuesListItemConfigObs, mergePdfMergeEntries, index$1 as onDbxModel, openEmbedDialog, openIframeDialog, openPdfPreviewDialog, openZipPreviewDialog, overrideClickElementEffect, pdfMergePageGroupKeyForSlotId, provideDbxDetachController, provideDbxFileUploadActionCompatable, provideDbxHelpServices, provideDbxLinkify, provideDbxListView, provideDbxListViewWrapper, provideDbxModelService, provideDbxPdfMergeEditorConfig, provideDbxPdfMergeEditorPreserveEntriesOnSlotDestroy, provideDbxProgressButtonGlobalConfig, provideDbxPromptConfirm, provideDbxRouterWebAngularRouterProviderConfig, provideDbxRouterWebUiRouterProviderConfig, provideDbxScreenMediaService, provideDbxStyleService, provideDbxValueListView, provideDbxValueListViewGroupDelegate, provideDbxValueListViewModifier, provideDbxWebFilePreviewServiceEntries, provideDbxWebPageTitleService, provideTwoColumnsContext, readPdfMergeEntryPageMetas, readPdfMergePageTag, readPdfMergeSidecar, registerHelpContextKeysWithDbxHelpContextService, resizeSignal, resolveSideNavDisplayMode, sanitizeDbxDialogContentConfig, screenMediaWidthTypeIsActive, splitPdfMergeSidecarDocuments, trackByModelKeyRef, trackByUniqueIdentifier, validatePdfMergeEntry, writePdfMergePageTag };
|
|
19812
21309
|
//# sourceMappingURL=dereekb-dbx-web.mjs.map
|