@acorex/platform 20.6.0-next.8 → 20.6.0-next.9
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.
|
@@ -8783,6 +8783,55 @@ class AXPFileListComponent {
|
|
|
8783
8783
|
this.displayFiles = computed(() => this.files(), ...(ngDevMode ? [{ debugName: "displayFiles" }] : []));
|
|
8784
8784
|
// Cache for per-file actions provided by hooks
|
|
8785
8785
|
this.fileIdToActions = signal({}, ...(ngDevMode ? [{ debugName: "fileIdToActions" }] : []));
|
|
8786
|
+
// Global actions from FileUploaderWebhookKeys.Actions hook
|
|
8787
|
+
this.globalActions = signal([], ...(ngDevMode ? [{ debugName: "globalActions" }] : []));
|
|
8788
|
+
// Clear cache when files change to reload actions
|
|
8789
|
+
this.filesChangeEffect = effect(() => {
|
|
8790
|
+
// Track files changes to clear cache
|
|
8791
|
+
this.files();
|
|
8792
|
+
this.fileIdToActions.set({});
|
|
8793
|
+
}, ...(ngDevMode ? [{ debugName: "filesChangeEffect" }] : []));
|
|
8794
|
+
}
|
|
8795
|
+
// Default actions that can be removed via hooks
|
|
8796
|
+
getDefaultActions(file) {
|
|
8797
|
+
const defaultActions = [];
|
|
8798
|
+
// Download action (always available if not readonly)
|
|
8799
|
+
if (!this.readonly()) {
|
|
8800
|
+
defaultActions.push({
|
|
8801
|
+
id: 'file-uploader.default.download',
|
|
8802
|
+
plugin: 'file-uploader',
|
|
8803
|
+
global: true,
|
|
8804
|
+
textKey: '@general:actions.download.title',
|
|
8805
|
+
icon: 'fa-light fa-download',
|
|
8806
|
+
run: async (ctx) => {
|
|
8807
|
+
await this.handleFileDownload({ nativeEvent: { preventDefault: () => { }, stopPropagation: () => { } } }, file);
|
|
8808
|
+
},
|
|
8809
|
+
});
|
|
8810
|
+
}
|
|
8811
|
+
// Edit and Remove actions (if editable and not readonly)
|
|
8812
|
+
if (!this.readonly() && (this.fileEditable() || (!this.fileEditable() && file.status === 'attached'))) {
|
|
8813
|
+
defaultActions.push({
|
|
8814
|
+
id: 'file-uploader.default.edit',
|
|
8815
|
+
plugin: 'file-uploader',
|
|
8816
|
+
global: true,
|
|
8817
|
+
textKey: '@general:actions.edit.title',
|
|
8818
|
+
icon: 'fa-light fa-pencil',
|
|
8819
|
+
run: async (ctx) => {
|
|
8820
|
+
await this.handleFileEdit({ stopPropagation: () => { }, preventDefault: () => { } }, file);
|
|
8821
|
+
},
|
|
8822
|
+
});
|
|
8823
|
+
defaultActions.push({
|
|
8824
|
+
id: 'file-uploader.default.remove',
|
|
8825
|
+
plugin: 'file-uploader',
|
|
8826
|
+
global: true,
|
|
8827
|
+
textKey: '@general:actions.delete.title',
|
|
8828
|
+
icon: 'fa-light fa-trash-can',
|
|
8829
|
+
run: async (ctx) => {
|
|
8830
|
+
await this.handleFileRemove({ nativeEvent: { preventDefault: () => { }, stopPropagation: () => { } } }, file);
|
|
8831
|
+
},
|
|
8832
|
+
});
|
|
8833
|
+
}
|
|
8834
|
+
return defaultActions;
|
|
8786
8835
|
}
|
|
8787
8836
|
actionsFor(file, index) {
|
|
8788
8837
|
const key = file.id ?? String(index);
|
|
@@ -8796,7 +8845,12 @@ class AXPFileListComponent {
|
|
|
8796
8845
|
}
|
|
8797
8846
|
async loadActionsFor(file, index) {
|
|
8798
8847
|
const key = file.id ?? String(index);
|
|
8799
|
-
|
|
8848
|
+
// Start with default actions
|
|
8849
|
+
const defaultActions = this.getDefaultActions(file);
|
|
8850
|
+
// Get global actions from FileUploaderWebhookKeys.Actions hook
|
|
8851
|
+
const globalActions = this.globalActions();
|
|
8852
|
+
// Get per-file actions from FileUploaderWebhookKeys.FileItem.Actions hook
|
|
8853
|
+
const fileItemPayload = await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.Actions, {
|
|
8800
8854
|
host: this.host(),
|
|
8801
8855
|
item: file,
|
|
8802
8856
|
index,
|
|
@@ -8805,14 +8859,55 @@ class AXPFileListComponent {
|
|
|
8805
8859
|
capabilities: {},
|
|
8806
8860
|
actions: [],
|
|
8807
8861
|
});
|
|
8862
|
+
// Combine all actions: default + global + file-item specific
|
|
8863
|
+
const allActions = [...defaultActions, ...globalActions, ...(fileItemPayload?.actions ?? [])];
|
|
8864
|
+
// Filter actions based on plugins and excludePlugins
|
|
8865
|
+
const enabledPlugins = this.plugins()?.map((p) => p.name) ?? [];
|
|
8866
|
+
const excludedPlugins = this.excludePlugins() ?? [];
|
|
8867
|
+
const filteredActions = allActions.filter((action) => {
|
|
8868
|
+
// If action has an id and is in excluded list, remove it
|
|
8869
|
+
if (action.id && excludedPlugins.some((excluded) => action.id?.includes(excluded))) {
|
|
8870
|
+
return false;
|
|
8871
|
+
}
|
|
8872
|
+
// If plugin is excluded, remove it
|
|
8873
|
+
if (excludedPlugins.includes(action.plugin)) {
|
|
8874
|
+
return false;
|
|
8875
|
+
}
|
|
8876
|
+
// If no enabled plugins, only show global actions
|
|
8877
|
+
if (!enabledPlugins || enabledPlugins.length === 0) {
|
|
8878
|
+
return action.global === true;
|
|
8879
|
+
}
|
|
8880
|
+
// Show if plugin is enabled or action is global
|
|
8881
|
+
return enabledPlugins.includes(action.plugin) || action.global === true;
|
|
8882
|
+
});
|
|
8808
8883
|
const next = { ...this.fileIdToActions() };
|
|
8809
|
-
next[key] =
|
|
8884
|
+
next[key] = filteredActions;
|
|
8810
8885
|
this.fileIdToActions.set(next);
|
|
8811
8886
|
}
|
|
8812
8887
|
async ngOnInit() {
|
|
8813
8888
|
this.fileTypes.set(await this.fileTypeService.items());
|
|
8889
|
+
await this.loadGlobalActions();
|
|
8814
8890
|
this.isLoading.set(false);
|
|
8815
8891
|
}
|
|
8892
|
+
async loadGlobalActions() {
|
|
8893
|
+
if (!this.host()) {
|
|
8894
|
+
return;
|
|
8895
|
+
}
|
|
8896
|
+
try {
|
|
8897
|
+
const payload = await this.hooks.runAsync(FileUploaderWebhookKeys.Actions, {
|
|
8898
|
+
host: this.host(),
|
|
8899
|
+
plugins: this.plugins() ?? [],
|
|
8900
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
8901
|
+
capabilities: {},
|
|
8902
|
+
actions: [],
|
|
8903
|
+
});
|
|
8904
|
+
this.globalActions.set(payload?.actions ?? []);
|
|
8905
|
+
}
|
|
8906
|
+
catch (error) {
|
|
8907
|
+
console.error('Error loading global actions:', error);
|
|
8908
|
+
this.globalActions.set([]);
|
|
8909
|
+
}
|
|
8910
|
+
}
|
|
8816
8911
|
getFileInfo(fileName) {
|
|
8817
8912
|
const extension = fileName.split('.').pop()?.toLowerCase() || '';
|
|
8818
8913
|
const extensions = this.fileTypes().flatMap((t) => t.extensions);
|
|
@@ -9018,8 +9113,22 @@ class AXPFileListComponent {
|
|
|
9018
9113
|
console.error(e);
|
|
9019
9114
|
}
|
|
9020
9115
|
}
|
|
9116
|
+
getActionColor(action) {
|
|
9117
|
+
// Map default action IDs to colors
|
|
9118
|
+
if (action.id === 'file-uploader.default.download') {
|
|
9119
|
+
return 'primary';
|
|
9120
|
+
}
|
|
9121
|
+
if (action.id === 'file-uploader.default.edit') {
|
|
9122
|
+
return 'secondary';
|
|
9123
|
+
}
|
|
9124
|
+
if (action.id === 'file-uploader.default.remove') {
|
|
9125
|
+
return 'danger';
|
|
9126
|
+
}
|
|
9127
|
+
// Default color for custom actions
|
|
9128
|
+
return action.color ?? 'primary';
|
|
9129
|
+
}
|
|
9021
9130
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPFileListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9022
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", 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 }, 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
|
|
9131
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", 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 }, 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') {\n <!-- Revert button -->\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 {\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:
|
|
9023
9132
|
// Angular
|
|
9024
9133
|
CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type:
|
|
9025
9134
|
// ACoreX
|
|
@@ -9042,7 +9151,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
9042
9151
|
AXTranslationModule,
|
|
9043
9152
|
// Platform
|
|
9044
9153
|
AXPStateMessageComponent,
|
|
9045
|
-
], providers: [], template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{ '--removed': file.status === 'deleted', '--attached': file.status === 'attached' }\"\n
|
|
9154
|
+
], 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') {\n <!-- Revert button -->\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 {\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"] }]
|
|
9046
9155
|
}], 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 }] }], 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 }] }] } });
|
|
9047
9156
|
|
|
9048
9157
|
class AXPFileUploaderWidgetService {
|