@acorex/platform 20.7.11 → 20.7.13

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.
@@ -84,10 +84,9 @@ import { AXDataTableModule } from '@acorex/components/data-table';
84
84
  import { AXPCommandExecutor, provideCommandSetups } from '@acorex/platform/runtime';
85
85
  import { AXUploaderZoneDirective } from '@acorex/cdk/uploader';
86
86
  import { AXUploaderModule } from '@acorex/components/uploader';
87
+ import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
87
88
  import * as i1$d from '@acorex/components/media-viewer';
88
89
  import { AXMediaViewerModule } from '@acorex/components/media-viewer';
89
- import * as i4$1 from '@acorex/components/menu';
90
- import { AXMenuModule } from '@acorex/components/menu';
91
90
  import * as i1$e from '@acorex/components/image';
92
91
  import { AXImageModule } from '@acorex/components/image';
93
92
  import * as i1$f from '@acorex/components/json-viewer';
@@ -10314,14 +10313,12 @@ class AXPEditFileUploaderCommand {
10314
10313
  const submitLabel = await this.translationService.translateAsync('@general:actions.submit.title');
10315
10314
  // Hooks: parameter insertion points
10316
10315
  const beforeRenamePayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Params.BeforeRename, {
10317
- host: this,
10318
10316
  file: file,
10319
10317
  plugins,
10320
10318
  excludePlugins,
10321
10319
  items: [],
10322
10320
  });
10323
10321
  const afterRenamePayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Params.AfterRename, {
10324
- host: this,
10325
10322
  file: file,
10326
10323
  plugins,
10327
10324
  excludePlugins,
@@ -10329,14 +10326,12 @@ class AXPEditFileUploaderCommand {
10329
10326
  });
10330
10327
  // Hooks: group insertion points around 'form'
10331
10328
  const groupsBeforePayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Groups.BeforeForm, {
10332
- host: this,
10333
10329
  file: file,
10334
10330
  plugins,
10335
10331
  excludePlugins,
10336
10332
  groups: [],
10337
10333
  });
10338
10334
  const groupsAfterPayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Groups.AfterForm, {
10339
- host: this,
10340
10335
  file: file,
10341
10336
  plugins,
10342
10337
  excludePlugins,
@@ -10565,8 +10560,8 @@ class AXPFileListComponent {
10565
10560
  // Plugin context (passed from parent widget)
10566
10561
  this.plugins = input(undefined, ...(ngDevMode ? [{ debugName: "plugins" }] : []));
10567
10562
  this.excludePlugins = input(undefined, ...(ngDevMode ? [{ debugName: "excludePlugins" }] : []));
10568
- // Host widget instance (optional) for plugins needing it
10569
- this.host = input(undefined, ...(ngDevMode ? [{ debugName: "host" }] : []));
10563
+ // Capabilities API for file operations (passed from parent widget)
10564
+ this.capabilities = input(undefined, ...(ngDevMode ? [{ debugName: "capabilities" }] : []));
10570
10565
  /**
10571
10566
  * All files should be displayed, even those with `deleted` status.
10572
10567
  * The template will handle the visual differences based on the status.
@@ -10641,12 +10636,11 @@ class AXPFileListComponent {
10641
10636
  // Get per-file actions from FileUploaderWebhookKeys.FileItem.Actions hook
10642
10637
  // Note: globalActions are only for toolbar, not for per-file actions
10643
10638
  const fileItemPayload = await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.Actions, {
10644
- host: this.host(),
10645
10639
  item: file,
10646
10640
  index,
10647
10641
  plugins: this.plugins() ?? [],
10648
10642
  excludePlugins: this.excludePlugins() ?? [],
10649
- capabilities: {},
10643
+ capabilities: this.capabilities(),
10650
10644
  actions: [],
10651
10645
  });
10652
10646
  // Combine all actions: default + file-item specific (exclude globalActions from per-file items)
@@ -10680,15 +10674,11 @@ class AXPFileListComponent {
10680
10674
  this.isLoading.set(false);
10681
10675
  }
10682
10676
  async loadGlobalActions() {
10683
- if (!this.host()) {
10684
- return;
10685
- }
10686
10677
  try {
10687
10678
  const payload = await this.hooks.runAsync(FileUploaderWebhookKeys.Actions, {
10688
- host: this.host(),
10689
10679
  plugins: this.plugins() ?? [],
10690
10680
  excludePlugins: this.excludePlugins() ?? [],
10691
- capabilities: {},
10681
+ capabilities: this.capabilities(),
10692
10682
  actions: [],
10693
10683
  });
10694
10684
  this.globalActions.set(payload?.actions ?? []);
@@ -10712,10 +10702,10 @@ class AXPFileListComponent {
10712
10702
  event.nativeEvent.stopPropagation();
10713
10703
  try {
10714
10704
  await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.BeforeDownload, {
10715
- host: this.host(),
10716
10705
  file,
10717
10706
  plugins: this.plugins() ?? [],
10718
10707
  excludePlugins: this.excludePlugins() ?? [],
10708
+ capabilities: this.capabilities(),
10719
10709
  });
10720
10710
  }
10721
10711
  catch { }
@@ -10788,11 +10778,11 @@ class AXPFileListComponent {
10788
10778
  if (file.source.value && typeof file.source.value === 'object' && 'id' in file.source.value && 'type' in file.source.value) {
10789
10779
  try {
10790
10780
  const result = await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.DownloadReference, {
10791
- host: this.host(),
10792
10781
  file,
10793
10782
  reference: file.source.value,
10794
10783
  plugins: this.plugins() ?? [],
10795
10784
  excludePlugins: this.excludePlugins() ?? [],
10785
+ capabilities: this.capabilities(),
10796
10786
  fileInfo: undefined,
10797
10787
  });
10798
10788
  if (result?.fileInfo) {
@@ -10832,10 +10822,10 @@ class AXPFileListComponent {
10832
10822
  }
10833
10823
  try {
10834
10824
  await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterDownload, {
10835
- host: this.host(),
10836
10825
  file,
10837
10826
  plugins: this.plugins() ?? [],
10838
10827
  excludePlugins: this.excludePlugins() ?? [],
10828
+ capabilities: this.capabilities(),
10839
10829
  });
10840
10830
  }
10841
10831
  catch { }
@@ -10846,10 +10836,10 @@ class AXPFileListComponent {
10846
10836
  this.onRemove.emit(file);
10847
10837
  try {
10848
10838
  await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterRemove, {
10849
- host: this.host(),
10850
10839
  file,
10851
10840
  plugins: this.plugins() ?? [],
10852
10841
  excludePlugins: this.excludePlugins() ?? [],
10842
+ capabilities: this.capabilities(),
10853
10843
  });
10854
10844
  }
10855
10845
  catch { }
@@ -10888,11 +10878,11 @@ class AXPFileListComponent {
10888
10878
  this.onRename.emit(newFile);
10889
10879
  try {
10890
10880
  await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterEdit, {
10891
- host: this.host(),
10892
10881
  file: newFile ?? file,
10893
10882
  previous: file,
10894
10883
  plugins: this.plugins() ?? [],
10895
10884
  excludePlugins: this.excludePlugins() ?? [],
10885
+ capabilities: this.capabilities(),
10896
10886
  });
10897
10887
  }
10898
10888
  catch { }
@@ -10900,7 +10890,7 @@ class AXPFileListComponent {
10900
10890
  ngOnDestroy() { }
10901
10891
  async runAction(action) {
10902
10892
  try {
10903
- await action.run({ host: this.host() });
10893
+ await action.run(this.capabilities());
10904
10894
  }
10905
10895
  catch (e) {
10906
10896
  console.error(e);
@@ -10921,7 +10911,7 @@ class AXPFileListComponent {
10921
10911
  return action.color ?? 'primary';
10922
10912
  }
10923
10913
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPFileListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10924
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPFileListComponent, isStandalone: true, selector: "axp-file-list", inputs: { readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, fileEditable: { classPropertyName: "fileEditable", publicName: "fileEditable", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, excludePlugins: { classPropertyName: "excludePlugins", publicName: "excludePlugins", isSignal: true, isRequired: false, transformFunction: null }, host: { classPropertyName: "host", publicName: "host", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onRemove: "onRemove", onRevert: "onRevert", onRename: "onRename" }, providers: [], ngImport: i0, template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{ '--removed': file.status === 'deleted', '--attached': file.status === 'attached' }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n <div class=\"__name\">{{ file.name }}</div>\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple()) {\n <!-- Revert button - only show when multiple is true -->\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event,file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n <!-- All actions from hooks (including default actions) -->\n @if (!readonly() || (readonly() && file.status === 'attached')) {\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n }\n </div>\n </div>\n} @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem;padding-top:.5rem;padding-bottom:.5rem}:host .__item{display:flex;cursor:pointer;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__actions{margin-left:auto;display:flex}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"], dependencies: [{ kind: "ngmodule", type:
10914
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPFileListComponent, isStandalone: true, selector: "axp-file-list", inputs: { readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, fileEditable: { classPropertyName: "fileEditable", publicName: "fileEditable", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, excludePlugins: { classPropertyName: "excludePlugins", publicName: "excludePlugins", isSignal: true, isRequired: false, transformFunction: null }, capabilities: { classPropertyName: "capabilities", publicName: "capabilities", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onRemove: "onRemove", onRevert: "onRevert", onRename: "onRename" }, providers: [], ngImport: i0, template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{ '--removed': file.status === 'deleted', '--attached': file.status === 'attached' }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n <div class=\"__name\">{{ file.name }}</div>\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple()) {\n <!-- Revert button - only show when multiple is true -->\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event,file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n <!-- All actions from hooks (including default actions) -->\n @if (!readonly() || (readonly() && file.status === 'attached')) {\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n }\n </div>\n </div>\n} @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem;padding-top:.5rem;padding-bottom:.5rem}:host .__item{display:flex;cursor:pointer;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__actions{margin-left:auto;display:flex}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"], dependencies: [{ kind: "ngmodule", type:
10925
10915
  // Angular
10926
10916
  CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type:
10927
10917
  // ACoreX
@@ -10945,7 +10935,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10945
10935
  // Platform
10946
10936
  AXPStateMessageComponent,
10947
10937
  ], providers: [], template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{ '--removed': file.status === 'deleted', '--attached': file.status === 'attached' }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n <div class=\"__name\">{{ file.name }}</div>\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple()) {\n <!-- Revert button - only show when multiple is true -->\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event,file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n <!-- All actions from hooks (including default actions) -->\n @if (!readonly() || (readonly() && file.status === 'attached')) {\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n }\n </div>\n </div>\n} @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem;padding-top:.5rem;padding-bottom:.5rem}:host .__item{display:flex;cursor:pointer;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__actions{margin-left:auto;display:flex}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"] }]
10948
- }], propDecorators: { onRemove: [{ type: i0.Output, args: ["onRemove"] }], onRevert: [{ type: i0.Output, args: ["onRevert"] }], onRename: [{ type: i0.Output, args: ["onRename"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], fileEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileEditable", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], excludePlugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludePlugins", required: false }] }], host: [{ type: i0.Input, args: [{ isSignal: true, alias: "host", required: false }] }] } });
10938
+ }], propDecorators: { onRemove: [{ type: i0.Output, args: ["onRemove"] }], onRevert: [{ type: i0.Output, args: ["onRevert"] }], onRename: [{ type: i0.Output, args: ["onRename"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], fileEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileEditable", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], excludePlugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludePlugins", required: false }] }], capabilities: [{ type: i0.Input, args: [{ isSignal: true, alias: "capabilities", required: false }] }] } });
10949
10939
 
10950
10940
  class AXPFileUploaderWidgetService {
10951
10941
  constructor() {
@@ -11115,7 +11105,6 @@ class AXPFileUploaderWidgetEditComponent extends AXPValueWidgetComponent {
11115
11105
  }
11116
11106
  async configureFromHooks() {
11117
11107
  const payload = await this.hooks.runAsync(FileUploaderWebhookKeys.Configure, {
11118
- host: this,
11119
11108
  plugins: this.plugins() ?? [],
11120
11109
  excludePlugins: this.excludePlugins() ?? [],
11121
11110
  capabilities: this.capabilities,
@@ -11152,16 +11141,13 @@ class AXPFileUploaderWidgetEditComponent extends AXPValueWidgetComponent {
11152
11141
  }));
11153
11142
  // allow plugins to transform items IN-PLACE before adding (no reassignment)
11154
11143
  await this.hooks.runAsync(FileUploaderWebhookKeys.BeforeFilesAdded, {
11155
- host: this,
11156
11144
  plugins: this.plugins() ?? [],
11157
11145
  excludePlugins: this.excludePlugins() ?? [],
11158
11146
  capabilities: this.capabilities,
11159
11147
  newFiles: fileUploadRequests,
11160
11148
  });
11161
11149
  this.setValue([...(this.getValue() ?? []), ...fileUploadRequests]);
11162
- console.log(this.getValue());
11163
11150
  await this.hooks.runAsync(FileUploaderWebhookKeys.AfterFilesAdded, {
11164
- host: this,
11165
11151
  plugins: this.plugins() ?? [],
11166
11152
  excludePlugins: this.excludePlugins() ?? [],
11167
11153
  capabilities: this.capabilities,
@@ -11271,15 +11257,14 @@ class AXPFileUploaderWidgetEditComponent extends AXPValueWidgetComponent {
11271
11257
  [fileEditable]="fileEditable()"
11272
11258
  [plugins]="plugins()"
11273
11259
  [excludePlugins]="excludePlugins()"
11274
- [host]="this"
11260
+ [capabilities]="capabilities"
11275
11261
  [multiple]="multiple()"
11276
11262
  (onRemove)="handleFileRemove($event)"
11277
11263
  (onRevert)="handleFileRevert($event)"
11278
11264
  (onRename)="handleFileRename($event)"
11279
- [readonly]="readonly()"
11280
11265
  ></axp-file-list>
11281
11266
  </div>
11282
- `, isInline: true, styles: [".__drag-over{background-color:rgba(var(--ax-sys-color-primary-50),.1);border:2px dashed rgb(var(--ax-sys-color-primary-500));border-radius:.375rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5$2.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPComponentSlotModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i5.AXTranslatorDirective, selector: "[translate]" }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "multiple", "files", "plugins", "excludePlugins", "host"], outputs: ["onRemove", "onRevert", "onRename"] }, { kind: "pipe", type: i1$3.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11267
+ `, isInline: true, styles: [".__drag-over{background-color:rgba(var(--ax-sys-color-primary-50),.1);border:2px dashed rgb(var(--ax-sys-color-primary-500));border-radius:.375rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5$2.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPComponentSlotModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i5.AXTranslatorDirective, selector: "[translate]" }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "multiple", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }, { kind: "pipe", type: i1$3.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11283
11268
  }
11284
11269
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPFileUploaderWidgetEditComponent, decorators: [{
11285
11270
  type: Component,
@@ -11323,12 +11308,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11323
11308
  [fileEditable]="fileEditable()"
11324
11309
  [plugins]="plugins()"
11325
11310
  [excludePlugins]="excludePlugins()"
11326
- [host]="this"
11311
+ [capabilities]="capabilities"
11327
11312
  [multiple]="multiple()"
11328
11313
  (onRemove)="handleFileRemove($event)"
11329
11314
  (onRevert)="handleFileRevert($event)"
11330
11315
  (onRename)="handleFileRename($event)"
11331
- [readonly]="readonly()"
11332
11316
  ></axp-file-list>
11333
11317
  </div>
11334
11318
  `, host: {
@@ -11369,7 +11353,7 @@ class AXPFileUploaderWidgetViewComponent extends AXPValueWidgetComponent {
11369
11353
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPFileUploaderWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
11370
11354
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AXPFileUploaderWidgetViewComponent, isStandalone: true, selector: "axp-file-uploader-widget-view", host: { properties: { "class": "this.__class" } }, usesInheritance: true, ngImport: i0, template: `
11371
11355
  <axp-file-list [files]="files()" [readonly]="true"></axp-file-list>
11372
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXUploaderModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "multiple", "files", "plugins", "excludePlugins", "host"], outputs: ["onRemove", "onRevert", "onRename"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11356
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXUploaderModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "multiple", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11373
11357
  }
11374
11358
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPFileUploaderWidgetViewComponent, decorators: [{
11375
11359
  type: Component,
@@ -11460,10 +11444,11 @@ class AXPGalleryWidgetEditComponent extends AXPValueWidgetComponent {
11460
11444
  constructor() {
11461
11445
  super(...arguments);
11462
11446
  this.thumbnail = computed(() => this.options()['thumbnail'], ...(ngDevMode ? [{ debugName: "thumbnail" }] : []));
11463
- this.height = computed(() => this.options()['height'], ...(ngDevMode ? [{ debugName: "height" }] : []));
11447
+ this.height = computed(() => this.options()['height'] ?? '30rem', ...(ngDevMode ? [{ debugName: "height" }] : []));
11464
11448
  this.header = computed(() => this.options()['header'], ...(ngDevMode ? [{ debugName: "header" }] : []));
11465
11449
  this.fileInfo = computed(() => this.options()['fileInfo'], ...(ngDevMode ? [{ debugName: "fileInfo" }] : []));
11466
- this.fullScreenButton = computed(() => this.options()['fullScreenButton'], ...(ngDevMode ? [{ debugName: "fullScreenButton" }] : []));
11450
+ this.fullScreenButton = computed(() => this.options()['fullScreenButton'] ?? true, ...(ngDevMode ? [{ debugName: "fullScreenButton" }] : []));
11451
+ this.downloadButton = computed(() => this.options()['downloadButton'] ?? true, ...(ngDevMode ? [{ debugName: "downloadButton" }] : []));
11467
11452
  this.allowUploadTypes = computed(() => this.options()['allowUploadTypes'] ?? [`image/*`], ...(ngDevMode ? [{ debugName: "allowUploadTypes" }] : []));
11468
11453
  this.allowUpload = computed(() => this.options()['allowUpload'] ?? true, ...(ngDevMode ? [{ debugName: "allowUpload" }] : []));
11469
11454
  this.plugins = computed(() => this.options()['plugins'] ?? [], ...(ngDevMode ? [{ debugName: "plugins" }] : []));
@@ -11472,6 +11457,8 @@ class AXPGalleryWidgetEditComponent extends AXPValueWidgetComponent {
11472
11457
  this.hooks = inject(AXPHookService);
11473
11458
  this.fileActionsService = inject(AXPFileActionsService);
11474
11459
  this.fileStorageService = inject(AXPFileStorageService);
11460
+ this.loadingDialog = inject(AXLoadingDialogService);
11461
+ this.translationService = inject(AXTranslationService);
11475
11462
  this.gallery = viewChild('c', ...(ngDevMode ? [{ debugName: "gallery" }] : []));
11476
11463
  /**
11477
11464
  * Convert AXPFileListItem[] to AXMediaViewerData[] for display in media viewer.
@@ -11545,6 +11532,41 @@ class AXPGalleryWidgetEditComponent extends AXPValueWidgetComponent {
11545
11532
  super.ngOnInit();
11546
11533
  this.loadActions();
11547
11534
  }
11535
+ /** Downloads the file at the current media viewer index. */
11536
+ async download() {
11537
+ const galleryRef = this.gallery();
11538
+ const files = this.getValue() ?? [];
11539
+ if (!galleryRef || files.length === 0) {
11540
+ return;
11541
+ }
11542
+ const index = galleryRef.getActiveIndex();
11543
+ const file = files[index];
11544
+ if (!file) {
11545
+ return;
11546
+ }
11547
+ const loadingDialogRef = this.loadingDialog.show({
11548
+ title: await this.translationService.translateAsync('@general:actions.download.processing'),
11549
+ mode: 'indeterminate',
11550
+ progressColor: 'primary',
11551
+ status: await this.translationService.translateAsync('@general:actions.download.processing'),
11552
+ });
11553
+ try {
11554
+ const url = await this.resolveFileUrl(file);
11555
+ if (!url) {
11556
+ return;
11557
+ }
11558
+ const link = document.createElement('a');
11559
+ link.href = url;
11560
+ link.download = file.name ?? 'download';
11561
+ link.target = '_blank';
11562
+ link.rel = 'noopener noreferrer';
11563
+ link.click();
11564
+ link.remove();
11565
+ }
11566
+ finally {
11567
+ loadingDialogRef.close();
11568
+ }
11569
+ }
11548
11570
  async loadActions() {
11549
11571
  const payload = {
11550
11572
  plugins: this.plugins() ?? [],
@@ -11673,29 +11695,27 @@ class AXPGalleryWidgetEditComponent extends AXPValueWidgetComponent {
11673
11695
  @if (allowUpload()) {
11674
11696
  <ax-prefix>
11675
11697
  <div *translate="let t">
11676
- <ax-menu
11677
- [openOn]="'click'"
11678
- [hasArrow]="true"
11679
- style="--ax-comp-menu-action-list-horizontal-min-width:auto"
11680
- >
11681
- <ax-menu-item>
11682
- <ax-prefix>
11683
- <ax-icon icon="fa-light fa-plus"></ax-icon>
11684
- </ax-prefix>
11685
- <ax-text>{{ (t('@general:actions.add-item.title') | async)! }}</ax-text>
11686
-
11687
- @for (action of fileActions(); track action.plugin) {
11688
- <ax-menu-item (onClick)="action.run(capabilities)">
11689
- @if (action.icon) {
11690
- <ax-prefix>
11691
- <ax-icon [icon]="action.icon"></ax-icon>
11692
- </ax-prefix>
11693
- }
11694
- <ax-text>{{ action.text ?? (action.textKey ? (t(action.textKey) | async)! : '') }}</ax-text>
11695
- </ax-menu-item>
11696
- }
11697
- </ax-menu-item>
11698
- </ax-menu>
11698
+ <ax-button class="ax-sm" [text]="(t('@general:actions.add-item.title') | async)!">
11699
+ <ax-prefix>
11700
+ <ax-icon icon="fa-light fa-plus"></ax-icon>
11701
+ </ax-prefix>
11702
+ <ax-dropdown-panel>
11703
+ <ax-button-item-list>
11704
+ @for (action of fileActions(); track action.plugin) {
11705
+ <ax-button-item
11706
+ (onClick)="action.run(capabilities)"
11707
+ [text]="action.text ?? (action.textKey ? (t(action.textKey) | async)! : '')"
11708
+ >
11709
+ @if (action.icon) {
11710
+ <ax-prefix>
11711
+ <ax-icon [icon]="action.icon"></ax-icon>
11712
+ </ax-prefix>
11713
+ }
11714
+ </ax-button-item>
11715
+ }
11716
+ </ax-button-item-list>
11717
+ </ax-dropdown-panel>
11718
+ </ax-button>
11699
11719
  </div>
11700
11720
  </ax-prefix>
11701
11721
  }
@@ -11705,6 +11725,11 @@ class AXPGalleryWidgetEditComponent extends AXPValueWidgetComponent {
11705
11725
  }
11706
11726
 
11707
11727
  <ax-suffix>
11728
+ @if (downloadButton()) {
11729
+ <ax-button class="ax-sm" look="blank" (click)="download()" [disabled]="mediaViewerData().length === 0">
11730
+ <ax-icon icon="fa-light fa-download"></ax-icon>
11731
+ </ax-button>
11732
+ }
11708
11733
  @if (fullScreenButton()) {
11709
11734
  <ax-fullscreen-button> </ax-fullscreen-button>
11710
11735
  }
@@ -11723,7 +11748,7 @@ class AXPGalleryWidgetEditComponent extends AXPValueWidgetComponent {
11723
11748
  <ax-icon class="rtl:ax-rotate-180 ax-icon ax-icon-chevron-left"> </ax-icon>
11724
11749
  </ax-button>
11725
11750
  </ax-suffix>
11726
- </ax-media-viewer-container>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXMediaViewerModule }, { kind: "component", type: i1$d.AXMediaViewerContainerComponent, selector: "ax-media-viewer-container", inputs: ["dataArray", "thumbnail", "pagination"] }, { kind: "component", type: i1$d.AXFileInfoComponent, selector: "ax-file-info" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i2.AXDecoratorFullScreenButtonComponent, selector: "ax-fullscreen-button", inputs: ["element", "isActive"], outputs: ["elementChange", "isActiveChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXMenuModule }, { kind: "component", type: i4$1.AXMenuItemComponent, selector: "ax-menu-item", inputs: ["name", "data", "disabled", "color"], outputs: ["onClick"] }, { kind: "component", type: i4$1.AXMenuComponent, selector: "ax-menu", inputs: ["orientation", "openOn", "closeOn", "items", "hasArrow"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i5.AXTranslatorDirective, selector: "[translate]" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11751
+ </ax-media-viewer-container>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXMediaViewerModule }, { kind: "component", type: i1$d.AXMediaViewerContainerComponent, selector: "ax-media-viewer-container", inputs: ["dataArray", "thumbnail", "pagination"] }, { kind: "component", type: i1$d.AXFileInfoComponent, selector: "ax-file-info" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i2.AXDecoratorFullScreenButtonComponent, selector: "ax-fullscreen-button", inputs: ["element", "isActive"], outputs: ["elementChange", "isActiveChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5$2.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i5.AXTranslatorDirective, selector: "[translate]" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11727
11752
  }
11728
11753
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPGalleryWidgetEditComponent, decorators: [{
11729
11754
  type: Component,
@@ -11735,29 +11760,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11735
11760
  @if (allowUpload()) {
11736
11761
  <ax-prefix>
11737
11762
  <div *translate="let t">
11738
- <ax-menu
11739
- [openOn]="'click'"
11740
- [hasArrow]="true"
11741
- style="--ax-comp-menu-action-list-horizontal-min-width:auto"
11742
- >
11743
- <ax-menu-item>
11744
- <ax-prefix>
11745
- <ax-icon icon="fa-light fa-plus"></ax-icon>
11746
- </ax-prefix>
11747
- <ax-text>{{ (t('@general:actions.add-item.title') | async)! }}</ax-text>
11748
-
11749
- @for (action of fileActions(); track action.plugin) {
11750
- <ax-menu-item (onClick)="action.run(capabilities)">
11751
- @if (action.icon) {
11752
- <ax-prefix>
11753
- <ax-icon [icon]="action.icon"></ax-icon>
11754
- </ax-prefix>
11755
- }
11756
- <ax-text>{{ action.text ?? (action.textKey ? (t(action.textKey) | async)! : '') }}</ax-text>
11757
- </ax-menu-item>
11758
- }
11759
- </ax-menu-item>
11760
- </ax-menu>
11763
+ <ax-button class="ax-sm" [text]="(t('@general:actions.add-item.title') | async)!">
11764
+ <ax-prefix>
11765
+ <ax-icon icon="fa-light fa-plus"></ax-icon>
11766
+ </ax-prefix>
11767
+ <ax-dropdown-panel>
11768
+ <ax-button-item-list>
11769
+ @for (action of fileActions(); track action.plugin) {
11770
+ <ax-button-item
11771
+ (onClick)="action.run(capabilities)"
11772
+ [text]="action.text ?? (action.textKey ? (t(action.textKey) | async)! : '')"
11773
+ >
11774
+ @if (action.icon) {
11775
+ <ax-prefix>
11776
+ <ax-icon [icon]="action.icon"></ax-icon>
11777
+ </ax-prefix>
11778
+ }
11779
+ </ax-button-item>
11780
+ }
11781
+ </ax-button-item-list>
11782
+ </ax-dropdown-panel>
11783
+ </ax-button>
11761
11784
  </div>
11762
11785
  </ax-prefix>
11763
11786
  }
@@ -11767,6 +11790,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11767
11790
  }
11768
11791
 
11769
11792
  <ax-suffix>
11793
+ @if (downloadButton()) {
11794
+ <ax-button class="ax-sm" look="blank" (click)="download()" [disabled]="mediaViewerData().length === 0">
11795
+ <ax-icon icon="fa-light fa-download"></ax-icon>
11796
+ </ax-button>
11797
+ }
11770
11798
  @if (fullScreenButton()) {
11771
11799
  <ax-fullscreen-button> </ax-fullscreen-button>
11772
11800
  }
@@ -11791,7 +11819,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11791
11819
  '[style.height]': 'height()',
11792
11820
  },
11793
11821
  changeDetection: ChangeDetectionStrategy.OnPush,
11794
- imports: [AXMediaViewerModule, AXDecoratorModule, AXButtonModule, AXMenuModule, AXTranslationModule, AsyncPipe],
11822
+ imports: [AXMediaViewerModule, AXDecoratorModule, AXButtonModule, AXDropdownModule, AXTranslationModule, AsyncPipe],
11795
11823
  }]
11796
11824
  }], propDecorators: { gallery: [{ type: i0.ViewChild, args: ['c', { isSignal: true }] }] } });
11797
11825
 
@@ -11803,12 +11831,17 @@ var galleryWidgetEdit_component = /*#__PURE__*/Object.freeze({
11803
11831
  class AXPGalleryWidgetViewComponent extends AXPValueWidgetComponent {
11804
11832
  constructor() {
11805
11833
  super(...arguments);
11834
+ this.loadingDialog = inject(AXLoadingDialogService);
11835
+ this.translationService = inject(AXTranslationService);
11806
11836
  this.internalValue = computed(() => {
11807
11837
  const parsedValue = this.getValue() ? JSON.parse(this.getValue()) : [];
11808
11838
  return Array.isArray(parsedValue) ? parsedValue.map((c) => c) : [parsedValue];
11809
11839
  }, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
11810
11840
  this.multiple = computed(() => this.options()['multiple'], ...(ngDevMode ? [{ debugName: "multiple" }] : []));
11811
- this.height = computed(() => this.options()['height'], ...(ngDevMode ? [{ debugName: "height" }] : []));
11841
+ this.height = computed(() => this.options()['height'] ?? 30, ...(ngDevMode ? [{ debugName: "height" }] : []));
11842
+ this.header = computed(() => this.options()['header'] ?? true, ...(ngDevMode ? [{ debugName: "header" }] : []));
11843
+ this.fullScreenButton = computed(() => this.options()['fullScreenButton'] ?? true, ...(ngDevMode ? [{ debugName: "fullScreenButton" }] : []));
11844
+ this.downloadButton = computed(() => this.options()['downloadButton'] ?? true, ...(ngDevMode ? [{ debugName: "downloadButton" }] : []));
11812
11845
  this.thumbnails = computed(() => this.internalValue().filter((i) => i.id !== this.activeMedia()?.id), ...(ngDevMode ? [{ debugName: "thumbnails" }] : []));
11813
11846
  this.activeMedia = signal(undefined, ...(ngDevMode ? [{ debugName: "activeMedia" }] : []));
11814
11847
  }
@@ -11818,6 +11851,30 @@ class AXPGalleryWidgetViewComponent extends AXPValueWidgetComponent {
11818
11851
  changeMediaSelected(media) {
11819
11852
  this.activeMedia.set(media);
11820
11853
  }
11854
+ /** Downloads the currently active media file. */
11855
+ async download() {
11856
+ const media = this.activeMedia();
11857
+ if (!media?.url) {
11858
+ return;
11859
+ }
11860
+ const loadingDialogRef = this.loadingDialog.show({
11861
+ title: await this.translationService.translateAsync('@general:actions.download.processing'),
11862
+ mode: 'indeterminate',
11863
+ progressColor: 'primary',
11864
+ status: await this.translationService.translateAsync('@general:actions.download.processing'),
11865
+ });
11866
+ try {
11867
+ const link = document.createElement('a');
11868
+ link.href = media.url;
11869
+ link.download = media.name ?? 'download';
11870
+ link.target = '_blank';
11871
+ link.rel = 'noopener noreferrer';
11872
+ link.click();
11873
+ }
11874
+ finally {
11875
+ loadingDialogRef.close();
11876
+ }
11877
+ }
11821
11878
  getMediaInfo(mediaName) {
11822
11879
  const extension = mediaName.split('.').pop()?.toLowerCase();
11823
11880
  switch (extension) {
@@ -11844,6 +11901,18 @@ class AXPGalleryWidgetViewComponent extends AXPValueWidgetComponent {
11844
11901
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPGalleryWidgetViewComponent, isStandalone: true, selector: "axp-gallery-widget-view", host: { properties: { "style.height": "height()" }, classAttribute: "ax-block" }, usesInheritance: true, ngImport: i0, template: `
11845
11902
  <div class="ax-grid ax-grid-cols-12 ax-gap-2">
11846
11903
  @if (internalValue().length) {
11904
+ @if (header()) {
11905
+ <div class="ax-col-start-1 ax-col-end-13 ax-flex ax-justify-end ax-gap-2 ax-mb-2">
11906
+ @if (downloadButton()) {
11907
+ <ax-button class="ax-sm" look="blank" (click)="download()">
11908
+ <ax-icon icon="fa-light fa-download"></ax-icon>
11909
+ </ax-button>
11910
+ }
11911
+ @if (fullScreenButton()) {
11912
+ <ax-fullscreen-button></ax-fullscreen-button>
11913
+ }
11914
+ </div>
11915
+ }
11847
11916
  <div class="ax-flex ax-items-center ax-justify-between ax-rounded-lg ax-col-start-1 ax-col-end-13">
11848
11917
  <div class="ax-flex ax-items-center md:ax-justify-start ax-justify-center ax-w-full ax-gap-3">
11849
11918
  <div class="ax-flex ax-flex-col ax-items-center ax-gap-4">
@@ -11905,7 +11974,7 @@ class AXPGalleryWidgetViewComponent extends AXPValueWidgetComponent {
11905
11974
  </axp-state-message>
11906
11975
  }
11907
11976
  </div>
11908
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i1$e.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11977
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i1$e.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: i2.AXDecoratorFullScreenButtonComponent, selector: "ax-fullscreen-button", inputs: ["element", "isActive"], outputs: ["elementChange", "isActiveChange"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXMediaViewerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11909
11978
  }
11910
11979
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPGalleryWidgetViewComponent, decorators: [{
11911
11980
  type: Component,
@@ -11914,6 +11983,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11914
11983
  template: `
11915
11984
  <div class="ax-grid ax-grid-cols-12 ax-gap-2">
11916
11985
  @if (internalValue().length) {
11986
+ @if (header()) {
11987
+ <div class="ax-col-start-1 ax-col-end-13 ax-flex ax-justify-end ax-gap-2 ax-mb-2">
11988
+ @if (downloadButton()) {
11989
+ <ax-button class="ax-sm" look="blank" (click)="download()">
11990
+ <ax-icon icon="fa-light fa-download"></ax-icon>
11991
+ </ax-button>
11992
+ }
11993
+ @if (fullScreenButton()) {
11994
+ <ax-fullscreen-button></ax-fullscreen-button>
11995
+ }
11996
+ </div>
11997
+ }
11917
11998
  <div class="ax-flex ax-items-center ax-justify-between ax-rounded-lg ax-col-start-1 ax-col-end-13">
11918
11999
  <div class="ax-flex ax-items-center md:ax-justify-start ax-justify-center ax-w-full ax-gap-3">
11919
12000
  <div class="ax-flex ax-flex-col ax-items-center ax-gap-4">
@@ -11981,7 +12062,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
11981
12062
  '[style.height]': 'height()',
11982
12063
  },
11983
12064
  changeDetection: ChangeDetectionStrategy.OnPush,
11984
- imports: [AXImageModule, AXDecoratorModule, AXLoadingModule, AXPStateMessageComponent],
12065
+ imports: [
12066
+ AXImageModule,
12067
+ AXDecoratorModule,
12068
+ AXLoadingModule,
12069
+ AXPStateMessageComponent,
12070
+ AXButtonModule,
12071
+ AXMediaViewerModule,
12072
+ ],
11985
12073
  }]
11986
12074
  }] });
11987
12075