@acorex/core 20.9.0 → 20.9.2

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.
@@ -1,5 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, runInInjectionContext, InjectionToken, inject, NgModule, DOCUMENT, isDevMode } from '@angular/core';
2
+ import { Injectable, runInInjectionContext, InjectionToken, inject, input, signal, effect, ChangeDetectionStrategy, Component, NgModule, DOCUMENT, isDevMode } from '@angular/core';
3
+ import { NgComponentOutlet } from '@angular/common';
3
4
  import * as i2 from '@acorex/core/validation';
4
5
  import { AXValidationService, AXValidationRegistryService, AXValidationModule } from '@acorex/core/validation';
5
6
  import * as i1 from '@acorex/core/format';
@@ -141,6 +142,18 @@ function getFileExtension(fileName) {
141
142
  return parts.length > 1 ? (parts.pop()?.toLowerCase() ?? '') : '';
142
143
  }
143
144
 
145
+ /** Resolves a {@link AXFileTypeComponent} to an Angular component type. */
146
+ async function resolveFileTypeComponentType(component) {
147
+ if (isLazyFileTypeComponentLoader(component)) {
148
+ return component();
149
+ }
150
+ return component;
151
+ }
152
+ /** Whether `value` is a lazy loader rather than an Angular component class. */
153
+ function isLazyFileTypeComponentLoader(value) {
154
+ return typeof value === 'function' && !('ɵcmp' in value);
155
+ }
156
+
144
157
  /** Matches a MIME type against a catalog pattern (`image/*`, exact type, …). */
145
158
  function matchesFileMime(mime, pattern) {
146
159
  const normalizedMime = mime.trim().toLowerCase();
@@ -208,6 +221,17 @@ function resolveFileTypeCopy(type, extension) {
208
221
  function resolveFileTypeOpen(type, extension) {
209
222
  return extension?.open ?? type.open;
210
223
  }
224
+ function resolveFileTypeComponent(type, extension) {
225
+ return extension?.component ?? type.component;
226
+ }
227
+ /** Loads the resolved view component (extension override first). */
228
+ async function loadFileTypeComponent(type, extension) {
229
+ const component = resolveFileTypeComponent(type, extension);
230
+ if (!component) {
231
+ return undefined;
232
+ }
233
+ return resolveFileTypeComponentType(component);
234
+ }
211
235
  function resolveFileTypePaste(type, extension) {
212
236
  return extension?.paste ?? type.paste;
213
237
  }
@@ -249,6 +273,38 @@ function runFileTypeOpen(type, ctx, payload, extension) {
249
273
  return runInInjectionContext(ctx.injector, () => open(ctx, payload));
250
274
  }
251
275
 
276
+ /**
277
+
278
+ * Builds an {@link AXFileType} with optional typed handlers and view component.
279
+
280
+ *
281
+
282
+ * @example
283
+
284
+ * ```ts
285
+
286
+ * export const imageFileType = defineFileType<ImagePayload>({
287
+
288
+ * name: 'image',
289
+
290
+ * copy: (p) => p.caption ?? p.url,
291
+
292
+ * component: () => import('./image-viewer.component').then(m => m.ImageViewerComponent),
293
+
294
+ * });
295
+
296
+ * ```
297
+
298
+ */
299
+ function defineFileType(config) {
300
+ const { copy, open, ...rest } = config;
301
+ return {
302
+ ...rest,
303
+ ...(copy ? { copy: copy } : {}),
304
+ ...(open ? { open: open } : {}),
305
+ };
306
+ }
307
+
252
308
  /** Contributes {@link AXFileType} entries (multi provider). */
253
309
  class AXFileTypeInfoProvider {
254
310
  }
@@ -261,6 +317,22 @@ function provideFileTypeInfoProvider(provider) {
261
317
  };
262
318
  }
263
319
 
320
+ /** Registers static file types via the existing {@link AXFileTypeInfoProvider} multi-token. */
321
+ function provideFileTypes(types) {
322
+ class AXStaticFileTypeProvider extends AXFileTypeInfoProvider {
323
+ items() {
324
+ const value = typeof types === 'function' ? types() : types;
325
+ return Promise.resolve(value);
326
+ }
327
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXStaticFileTypeProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
328
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXStaticFileTypeProvider }); }
329
+ }
330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXStaticFileTypeProvider, decorators: [{
331
+ type: Injectable
332
+ }] });
333
+ return provideFileTypeInfoProvider(AXStaticFileTypeProvider);
334
+ }
335
+
264
336
  /** Maps {@link AXFileValidations} to executable validation entries. */
265
337
  function fileValidationsToRules(validations) {
266
338
  const rules = [];
@@ -369,6 +441,24 @@ class AXFileTypeRegistryService {
369
441
  const types = await this.resolveTypes();
370
442
  return types.find((t) => t.name === name);
371
443
  }
444
+ /** Resolved view component ref for a catalog entry (optional extension hint). */
445
+ async getComponent(name, hint) {
446
+ const type = await this.get(name);
447
+ if (!type) {
448
+ return undefined;
449
+ }
450
+ const extension = hint ? resolveFileTypeExtension(type, hint) : undefined;
451
+ return resolveFileTypeComponent(type, extension);
452
+ }
453
+ /** Loads the view component for a catalog entry. */
454
+ async loadComponent(name, hint) {
455
+ const type = await this.get(name);
456
+ if (!type) {
457
+ return undefined;
458
+ }
459
+ const extension = hint ? resolveFileTypeExtension(type, hint) : undefined;
460
+ return loadFileTypeComponent(type, extension);
461
+ }
372
462
  async accept(typeNames) {
373
463
  const names = typeNames ? (Array.isArray(typeNames) ? typeNames : [typeNames]) : undefined;
374
464
  const types = await this.resolveTypes();
@@ -449,6 +539,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
449
539
  args: [{ providedIn: 'root' }]
450
540
  }] });
451
541
 
542
+ class AXFileTypeRendererOutletComponent {
543
+ constructor() {
544
+ this.registry = inject(AXFileTypeRegistryService);
545
+ /** Catalog name (e.g. `image`, `conversation-image`). */
546
+ this.fileType = input.required(...(ngDevMode ? [{ debugName: "fileType" }] : []));
547
+ /** Payload passed to the file-type view component. */
548
+ this.payload = input.required(...(ngDevMode ? [{ debugName: "payload" }] : []));
549
+ /** Optional extension / MIME hint for extension-level renderer overrides. */
550
+ this.extensionHint = input(...(ngDevMode ? [undefined, { debugName: "extensionHint" }] : []));
551
+ /** Extra inputs merged with `payload` (e.g. `message` in conversation). */
552
+ this.inputs = input({}, ...(ngDevMode ? [{ debugName: "inputs" }] : []));
553
+ this.resolvedComponent = signal(null, ...(ngDevMode ? [{ debugName: "resolvedComponent" }] : []));
554
+ this.loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
555
+ this.mergedInputs = signal({}, ...(ngDevMode ? [{ debugName: "mergedInputs" }] : []));
556
+ effect(() => {
557
+ const name = this.fileType();
558
+ const payload = this.payload();
559
+ const hint = this.extensionHint();
560
+ const extra = this.inputs();
561
+ this.mergedInputs.set({ payload, ...extra });
562
+ this.loading.set(true);
563
+ this.resolvedComponent.set(null);
564
+ void this.registry.loadComponent(name, hint).then((component) => {
565
+ this.resolvedComponent.set(component ?? null);
566
+ this.loading.set(false);
567
+ });
568
+ });
569
+ }
570
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXFileTypeRendererOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
571
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AXFileTypeRendererOutletComponent, isStandalone: true, selector: "ax-file-type-renderer", inputs: { fileType: { classPropertyName: "fileType", publicName: "fileType", isSignal: true, isRequired: true, transformFunction: null }, payload: { classPropertyName: "payload", publicName: "payload", isSignal: true, isRequired: true, transformFunction: null }, extensionHint: { classPropertyName: "extensionHint", publicName: "extensionHint", isSignal: true, isRequired: false, transformFunction: null }, inputs: { classPropertyName: "inputs", publicName: "inputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
572
+ @if (resolvedComponent(); as component) {
573
+ <ng-container *ngComponentOutlet="component; inputs: mergedInputs()" />
574
+ } @else if (loading()) {
575
+ <span class="ax-file-type-renderer__loading" aria-hidden="true"></span>
576
+ }
577
+ `, isInline: true, styles: [":host{display:contents}.ax-file-type-renderer__loading{display:inline-block;min-height:1rem;min-width:1rem}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
578
+ }
579
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXFileTypeRendererOutletComponent, decorators: [{
580
+ type: Component,
581
+ args: [{ selector: 'ax-file-type-renderer', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgComponentOutlet], template: `
582
+ @if (resolvedComponent(); as component) {
583
+ <ng-container *ngComponentOutlet="component; inputs: mergedInputs()" />
584
+ } @else if (loading()) {
585
+ <span class="ax-file-type-renderer__loading" aria-hidden="true"></span>
586
+ }
587
+ `, styles: [":host{display:contents}.ax-file-type-renderer__loading{display:inline-block;min-height:1rem;min-width:1rem}\n"] }]
588
+ }], ctorParameters: () => [] });
589
+
452
590
  const MB$4 = 1024 * 1024;
453
591
  const AX_AUDIO_FILE_TYPE = {
454
592
  name: 'audio',
@@ -864,5 +1002,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
864
1002
  * Generated bundle index. Do not edit.
865
1003
  */
866
1004
 
867
- export { AXAudioFileTypeProvider, AXDocumentFileTypeProvider, AXFileFileTypeProvider, AXFileMaxSizeValidationRule, AXFileMimeTypeValidationRule, AXFileMinSizeValidationRule, AXFileModule, AXFileService, AXFileSizeFormatter, AXFileTypeInfoProvider, AXFileTypeRegistryService, AXImageFileTypeProvider, AXVideoFileTypeProvider, AX_AUDIO_FILE_TYPE, AX_BUILTIN_FILE_TYPE_PROVIDERS, AX_DOCUMENT_FILE_TYPE, AX_FILE_FILE_TYPE, AX_FILE_TYPE_INFO_PROVIDER, AX_FILE_TYPE_METADATA_PURPOSE, AX_FILE_VALIDATION_RULES, AX_IMAGE_FILE_TYPE, AX_VIDEO_FILE_TYPE, createFileTypeMetadata, fileTypeMatchesPurpose, fileValidationsToRules, filterFileTypesByPurpose, findFileTypeExtension, findFileTypeExtensionByName, findFileTypeExtensionForMime, formatFileSizeBytes, getFileExtension, getFileTypePurposes, isEmptyFileValidations, isExtensionsOnlyType, matchesFileMime, mergeFileValidations, provideBuiltinFileTypeProviders, provideDefaultFileTypeProviders, provideFileTypeInfoProvider, provideFileTypeSystem, provideFileValidationRules, resolveFileCopyText, resolveFileTypeCopy, resolveFileTypeExtension, resolveFileTypeOpen, resolveFileTypePaste, resolveFileTypeUtility, resolveFileValidations, runFileTypeCopy, runFileTypeOpen, runFileTypeUtility, runFileTypeUtilityForFile, validateFileAgainstType, validateFileWithValidations };
1005
+ export { AXAudioFileTypeProvider, AXDocumentFileTypeProvider, AXFileFileTypeProvider, AXFileMaxSizeValidationRule, AXFileMimeTypeValidationRule, AXFileMinSizeValidationRule, AXFileModule, AXFileService, AXFileSizeFormatter, AXFileTypeInfoProvider, AXFileTypeRegistryService, AXFileTypeRendererOutletComponent, AXImageFileTypeProvider, AXVideoFileTypeProvider, AX_AUDIO_FILE_TYPE, AX_BUILTIN_FILE_TYPE_PROVIDERS, AX_DOCUMENT_FILE_TYPE, AX_FILE_FILE_TYPE, AX_FILE_TYPE_INFO_PROVIDER, AX_FILE_TYPE_METADATA_PURPOSE, AX_FILE_VALIDATION_RULES, AX_IMAGE_FILE_TYPE, AX_VIDEO_FILE_TYPE, createFileTypeMetadata, defineFileType, fileTypeMatchesPurpose, fileValidationsToRules, filterFileTypesByPurpose, findFileTypeExtension, findFileTypeExtensionByName, findFileTypeExtensionForMime, formatFileSizeBytes, getFileExtension, getFileTypePurposes, isEmptyFileValidations, isExtensionsOnlyType, isLazyFileTypeComponentLoader, loadFileTypeComponent, matchesFileMime, mergeFileValidations, provideBuiltinFileTypeProviders, provideDefaultFileTypeProviders, provideFileTypeInfoProvider, provideFileTypeSystem, provideFileTypes, provideFileValidationRules, resolveFileCopyText, resolveFileTypeComponent, resolveFileTypeComponentType, resolveFileTypeCopy, resolveFileTypeExtension, resolveFileTypeOpen, resolveFileTypePaste, resolveFileTypeUtility, resolveFileValidations, runFileTypeCopy, runFileTypeOpen, runFileTypeUtility, runFileTypeUtilityForFile, validateFileAgainstType, validateFileWithValidations };
868
1006
  //# sourceMappingURL=acorex-core-file.mjs.map