@acorex-platform/framework-client 1.0.1 → 2.0.0-next.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.
Files changed (19) hide show
  1. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +68 -18
  2. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-i9oZEXUR.mjs → acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-DToXUL54.mjs} +76 -30
  4. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-DToXUL54.mjs.map +1 -0
  5. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-DyrvSBEY.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-DqssRhIG.mjs} +10 -5
  6. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-DqssRhIG.mjs.map +1 -0
  7. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-B8s_JEV8.mjs → acorex-platform-framework-client-layout-entity-bulk-entity.command-Deq-a9MQ.mjs} +2 -2
  8. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-B8s_JEV8.mjs.map → acorex-platform-framework-client-layout-entity-bulk-entity.command-Deq-a9MQ.mjs.map} +1 -1
  9. package/fesm2022/{acorex-platform-framework-client-layout-entity-file-list-popup.component-C0LgUzzv.mjs → acorex-platform-framework-client-layout-entity-file-list-popup.component-J30yrJMo.mjs} +26 -10
  10. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-J30yrJMo.mjs.map +1 -0
  11. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-fATD9Uax.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-BqqxD8cX.mjs} +2 -2
  12. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-fATD9Uax.mjs.map → acorex-platform-framework-client-layout-entity-gallery-page.component-BqqxD8cX.mjs.map} +1 -1
  13. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +1 -1
  14. package/package.json +2 -2
  15. package/types/acorex-platform-framework-client-layout-components.d.ts +3 -3
  16. package/types/acorex-platform-framework-client-layout-entity.d.ts +20 -4
  17. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-i9oZEXUR.mjs.map +0 -1
  18. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-DyrvSBEY.mjs.map +0 -1
  19. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-C0LgUzzv.mjs.map +0 -1
@@ -5375,17 +5375,43 @@ function resolveFileChooserExtension(name) {
5375
5375
  //#region ----- Imports -----
5376
5376
  //#endregion
5377
5377
  //#region ----- URL resolution -----
5378
+ /**
5379
+ * Loads stored bytes through {@link AXPFileStorageService.download} and returns a `blob:` URL.
5380
+ * `<img>`, `<video>`, and PDF viewers do not send `Authorization`, so a raw download URL 401s
5381
+ * when the host authenticates with a bearer token instead of a cookie.
5382
+ */
5383
+ async function resolveStoredFileBlobUrl(fileId, fileStorageService, track, blobUrlCache) {
5384
+ const cached = blobUrlCache?.get(fileId);
5385
+ if (cached) {
5386
+ return cached;
5387
+ }
5388
+ const pending = fileStorageService
5389
+ .download(fileId)
5390
+ .then((blob) => {
5391
+ if (!(blob instanceof Blob)) {
5392
+ blobUrlCache?.delete(fileId);
5393
+ return '';
5394
+ }
5395
+ return track(URL.createObjectURL(blob));
5396
+ })
5397
+ .catch(() => {
5398
+ blobUrlCache?.delete(fileId);
5399
+ return '';
5400
+ });
5401
+ blobUrlCache?.set(fileId, pending);
5402
+ return pending;
5403
+ }
5378
5404
  /** Resolve a file list item to a display/download URL. */
5379
- async function resolveFileListItemUrl(file, fileStorageService, trackObjectUrl) {
5405
+ async function resolveFileListItemUrl(file, fileStorageService, trackObjectUrl, blobUrlCache) {
5380
5406
  const track = (url) => (trackObjectUrl ? trackObjectUrl(url) : url);
5381
5407
  if (file.source?.kind === 'blob' && file.source.value instanceof Blob) {
5382
5408
  return track(URL.createObjectURL(file.source.value));
5383
5409
  }
5384
5410
  if (file.source?.kind === 'fileId' && typeof file.source.value === 'string') {
5385
5411
  const fileId = normalizeStoredImageRef(file.source.value) ?? file.source.value;
5386
- // Preview uses the authenticated download URL (cookie session) — do not call getInfo(),
5387
- // which downloads the full binary and forces a network round-trip per slide.
5388
- return fileId ? fileStorageService.getDownloadUrl(fileId) : '';
5412
+ return fileId
5413
+ ? resolveStoredFileBlobUrl(fileId, fileStorageService, track, blobUrlCache)
5414
+ : '';
5389
5415
  }
5390
5416
  if (file.source?.kind === 'url' && typeof file.source.value === 'string') {
5391
5417
  return file.source.value;
@@ -5396,7 +5422,9 @@ async function resolveFileListItemUrl(file, fileStorageService, trackObjectUrl)
5396
5422
  return value;
5397
5423
  }
5398
5424
  const fileId = normalizeStoredImageRef(value) ?? value;
5399
- return fileId ? fileStorageService.getDownloadUrl(fileId) : '';
5425
+ return fileId
5426
+ ? resolveStoredFileBlobUrl(fileId, fileStorageService, track, blobUrlCache)
5427
+ : '';
5400
5428
  }
5401
5429
  return '';
5402
5430
  }
@@ -5438,8 +5466,9 @@ function normalizeFileViewerBoundValue(value) {
5438
5466
  return [value];
5439
5467
  }
5440
5468
  /** Maps platform files to viewer items using file storage resolution. */
5441
- function mapFileListToViewerItems(files, fileStorageService, trackObjectUrl) {
5442
- return files.map((file) => fileListItemToFileViewerItem(file, (item) => resolveFileListItemUrl(item, fileStorageService, trackObjectUrl)));
5469
+ function mapFileListToViewerItems(files, fileStorageService, trackObjectUrl, blobUrlCache) {
5470
+ const cache = blobUrlCache ?? new Map();
5471
+ return files.map((file) => fileListItemToFileViewerItem(file, (item) => resolveFileListItemUrl(item, fileStorageService, trackObjectUrl, cache)));
5443
5472
  }
5444
5473
  //#endregion
5445
5474
 
@@ -5521,6 +5550,7 @@ class AXPFileViewerComponent {
5521
5550
  //#endregion
5522
5551
  //#region ----- Lifecycle -----
5523
5552
  this.objectUrls = new Set();
5553
+ this.storedFileBlobUrls = new Map();
5524
5554
  }
5525
5555
  //#endregion
5526
5556
  //#region ----- Container API -----
@@ -5549,6 +5579,7 @@ class AXPFileViewerComponent {
5549
5579
  await this.container()?.downloadCurrent();
5550
5580
  }
5551
5581
  ngOnDestroy() {
5582
+ this.storedFileBlobUrls.clear();
5552
5583
  for (const url of this.objectUrls) {
5553
5584
  URL.revokeObjectURL(url);
5554
5585
  }
@@ -5567,7 +5598,7 @@ class AXPFileViewerComponent {
5567
5598
  if (typeof directUrl === 'string' && directUrl.length > 0) {
5568
5599
  return Promise.resolve(directUrl);
5569
5600
  }
5570
- return resolveFileListItemUrl(file, this.fileStorageService, (url) => this.trackObjectUrl(url));
5601
+ return resolveFileListItemUrl(file, this.fileStorageService, (url) => this.trackObjectUrl(url), this.storedFileBlobUrls);
5571
5602
  }
5572
5603
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPFileViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5573
5604
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.8", type: AXPFileViewerComponent, isStandalone: true, selector: "axp-file-viewer", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, thumbnail: { classPropertyName: "thumbnail", publicName: "thumbnail", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, download: { classPropertyName: "download", publicName: "download", isSignal: true, isRequired: false, transformFunction: null }, fullscreen: { classPropertyName: "fullscreen", publicName: "fullscreen", isSignal: true, isRequired: false, transformFunction: null }, galleryMode: { classPropertyName: "galleryMode", publicName: "galleryMode", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, headerContent: { classPropertyName: "headerContent", publicName: "headerContent", isSignal: true, isRequired: false, transformFunction: null }, headerContentInputs: { classPropertyName: "headerContentInputs", publicName: "headerContentInputs", isSignal: true, isRequired: false, transformFunction: null }, fallbackContent: { classPropertyName: "fallbackContent", publicName: "fallbackContent", isSignal: true, isRequired: false, transformFunction: null }, fallbackContentInputs: { classPropertyName: "fallbackContentInputs", publicName: "fallbackContentInputs", isSignal: true, isRequired: false, transformFunction: null }, emptyContent: { classPropertyName: "emptyContent", publicName: "emptyContent", isSignal: true, isRequired: false, transformFunction: null }, emptyContentInputs: { classPropertyName: "emptyContentInputs", publicName: "emptyContentInputs", isSignal: true, isRequired: false, transformFunction: null }, headerActions: { classPropertyName: "headerActions", publicName: "headerActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { headerActionClick: "headerActionClick", fullscreenClosed: "fullscreenClosed" }, host: { properties: { "style.height": "height()" }, classAttribute: "block" }, viewQueries: [{ propertyName: "container", first: true, predicate: AXFileViewerContainerComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<ax-file-viewer-container\n class=\"axp-file-viewer h-full w-full\"\n [items]=\"resolvedItems()\"\n [startIndex]=\"startIndex()\"\n [thumbnail]=\"thumbnail()\"\n [pagination]=\"pagination()\"\n [download]=\"download()\"\n [fullscreen]=\"fullscreen()\"\n [galleryMode]=\"galleryMode()\"\n [headerContent]=\"boundHeaderContent()\"\n [headerContentInputs]=\"boundHeaderContentInputs()\"\n [fallbackContent]=\"boundFallbackContent()\"\n [fallbackContentInputs]=\"boundFallbackContentInputs()\"\n [emptyContent]=\"boundEmptyContent()\"\n [emptyContentInputs]=\"boundEmptyContentInputs()\"\n [headerActions]=\"headerActions()\"\n [loading]=\"loading()\"\n (headerActionClick)=\"headerActionClick.emit($event)\"\n (fullscreenClosed)=\"fullscreenClosed.emit()\"\n/>\n", dependencies: [{ kind: "component", type: AXFileViewerContainerComponent, selector: "ax-file-viewer-container", inputs: ["items", "startIndex", "thumbnail", "pagination", "download", "fullscreen", "headerActions", "galleryMode", "headerContent", "headerContentInputs", "fallbackContent", "fallbackContentInputs", "emptyContent", "emptyContentInputs", "loading"], outputs: ["headerActionClick", "fullscreenClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
@@ -5666,11 +5697,36 @@ class AXPFileViewerService {
5666
5697
  */
5667
5698
  open(config) {
5668
5699
  const { files, items, ...rest } = config;
5669
- const resolvedItems = items ?? (files?.length ? this.mapFiles(files) : []);
5670
- return this.galleryService.open({
5700
+ let closed = false;
5701
+ const sessionUrls = new Set();
5702
+ const blobUrlCache = new Map();
5703
+ const track = (url) => {
5704
+ if (!url.startsWith('blob:')) {
5705
+ return url;
5706
+ }
5707
+ if (closed) {
5708
+ URL.revokeObjectURL(url);
5709
+ return url;
5710
+ }
5711
+ sessionUrls.add(url);
5712
+ this.objectUrls.add(url);
5713
+ return url;
5714
+ };
5715
+ const resolvedItems = items ?? (files?.length ? this.mapFiles(files, track, blobUrlCache) : []);
5716
+ const closedPromise = this.galleryService.open({
5671
5717
  ...rest,
5672
5718
  items: resolvedItems,
5673
5719
  });
5720
+ void closedPromise.then(() => {
5721
+ closed = true;
5722
+ blobUrlCache.clear();
5723
+ for (const url of sessionUrls) {
5724
+ URL.revokeObjectURL(url);
5725
+ this.objectUrls.delete(url);
5726
+ }
5727
+ sessionUrls.clear();
5728
+ });
5729
+ return closedPromise;
5674
5730
  }
5675
5731
  /**
5676
5732
  * Opens a gallery from platform file list items.
@@ -5688,14 +5744,8 @@ class AXPFileViewerService {
5688
5744
  }
5689
5745
  //#endregion
5690
5746
  //#region ----- Mapping -----
5691
- mapFiles(files) {
5692
- return mapFileListToViewerItems(files, this.fileStorageService, (url) => this.trackObjectUrl(url));
5693
- }
5694
- trackObjectUrl(url) {
5695
- if (url.startsWith('blob:')) {
5696
- this.objectUrls.add(url);
5697
- }
5698
- return url;
5747
+ mapFiles(files, trackObjectUrl, blobUrlCache) {
5748
+ return mapFileListToViewerItems(files, this.fileStorageService, trackObjectUrl, blobUrlCache);
5699
5749
  }
5700
5750
  revokeTrackedUrls() {
5701
5751
  for (const url of this.objectUrls) {