@acorex/platform 21.0.0-next.65 → 21.0.0-next.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/acorex-platform-common.mjs +94 -18
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +42 -1
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +29 -7
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +282 -108
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-BaTS183I.mjs +383 -0
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-BaTS183I.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs → acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs} +15 -18
- package/fesm2022/acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity.mjs +3339 -301
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +0 -1
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +1377 -4
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widgets.mjs +9214 -11991
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs → acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs} +3 -3
- package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs.map → acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs → acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs} +3 -3
- package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs.map → acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs} +4 -4
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs.map → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map} +1 -1
- package/fesm2022/acorex-platform-themes-default.mjs +685 -287
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-shared-settings.provider-BgXYCFia.mjs → acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs} +29 -2
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared.mjs +94 -24
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +176 -26
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-common.d.ts +73 -9
- package/types/acorex-platform-core.d.ts +63 -2
- package/types/acorex-platform-layout-builder.d.ts +7 -1
- package/types/acorex-platform-layout-components.d.ts +162 -36
- package/types/acorex-platform-layout-entity.d.ts +704 -14
- package/types/acorex-platform-layout-views.d.ts +28 -0
- package/types/acorex-platform-layout-widget-core.d.ts +156 -3
- package/types/acorex-platform-layout-widgets.d.ts +29 -393
- package/types/acorex-platform-themes-default.d.ts +137 -30
- package/types/acorex-platform-themes-shared.d.ts +23 -1
- package/types/acorex-platform-workflow.d.ts +89 -4
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BgXYCFia.mjs.map +0 -1
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { AXToastService } from '@acorex/components/toast';
|
|
2
2
|
import * as i6 from '@acorex/core/translation';
|
|
3
|
-
import { AXTranslationService, AXTranslationModule, resolveMultiLanguageString } from '@acorex/core/translation';
|
|
3
|
+
import { AXTranslationService, AXTranslationModule, resolveMultiLanguageString, translateSync } from '@acorex/core/translation';
|
|
4
4
|
import * as i4$4 from '@acorex/platform/common';
|
|
5
|
-
import { AXPEntityCommandScope, AXPSettingsService, AXPCommonSettings, AXPFilterOperatorMiddlewareService, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCleanNestedFilters, AXPDefaultMultiLanguageConfigService, withDefaultMultiLanguageOnWidgetNodeTree, AXPEntityQueryType, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER, AXPMenuItemsDataSourceDefinition } from '@acorex/platform/common';
|
|
5
|
+
import { AXPEntityCommandScope, AXPSettingsService, AXPCommonSettings, AXPFilterOperatorMiddlewareService, isCardFieldBadgeDisplay, resolveCardFieldBadgeColor, getEntityInfo, resolveEnabledMasterListLayouts, resolveDefaultMasterListLayout, AXPRefreshEvent, AXPReloadEvent, AXPCleanNestedFilters, AXPFileTypeProviderService, AXPFileStorageService, AXPFileActionsService, AXPDefaultMultiLanguageConfigService, withDefaultMultiLanguageOnWidgetNodeTree, AXPEntityQueryType, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER, AXPMenuItemsDataSourceDefinition } from '@acorex/platform/common';
|
|
6
6
|
import * as i0 from '@angular/core';
|
|
7
|
-
import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, computed, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, ChangeDetectorRef, effect, Input, afterNextRender, untracked, ViewEncapsulation, viewChildren, linkedSignal, HostBinding, output,
|
|
7
|
+
import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, computed, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, NgModule, ChangeDetectorRef, effect, Input, afterNextRender, untracked, ViewEncapsulation, viewChildren, linkedSignal, HostBinding, output, makeEnvironmentProviders } from '@angular/core';
|
|
8
8
|
import { Subject, takeUntil } from 'rxjs';
|
|
9
9
|
import { AXPLayoutBuilderService, LayoutBuilderModule } from '@acorex/platform/layout/builder';
|
|
10
10
|
import * as i3 from '@acorex/platform/layout/widget-core';
|
|
11
|
-
import { AXPWidgetsCatalog, AXPWidgetCoreModule, AXPPageStatus, AXPWidgetRegistryService, AXPColumnWidgetComponent, AXPValueWidgetComponent, AXPWidgetGroupEnum, createBooleanProperty, AXPWidgetRendererDirective,
|
|
12
|
-
import { AXPSystemActionType, AXPDeviceService, AXPExpressionEvaluatorService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, objectKeyValueTransforms, AXPColumnWidthService, AXPModuleManifestRegistry, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPModuleManifestsDataSourceDefinition } from '@acorex/platform/core';
|
|
13
|
-
import { cloneDeep, merge, get, castArray, set, orderBy, omit, isNil, isEmpty, isEqual as isEqual$1 } from 'lodash-es';
|
|
11
|
+
import { AXPWidgetsCatalog, AXPWidgetCoreModule, AXPPageStatus, AXPWidgetRegistryService, AXPColumnWidgetComponent, AXPValueWidgetComponent, AXPWidgetGroupEnum, createBooleanProperty, AXPWidgetRendererDirective, createStringProperty, createNumberProperty, AXP_WIDGETS_ADVANCE_SUB_MEDIA, AXP_WIDGETS_ADVANCE_CATEGORY, createSelectProperty, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
|
|
12
|
+
import { AXPSystemActionType, AXPDeviceService, AXPExpressionEvaluatorService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, objectKeyValueTransforms, AXPHookService, AXPDataGenerator, AXPComponentSlotModule, AXPColumnWidthService, AXPModuleManifestRegistry, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPModuleManifestsDataSourceDefinition } from '@acorex/platform/core';
|
|
13
|
+
import { cloneDeep, merge, get, castArray, set, orderBy, omit, isNil, isEmpty, isEqual as isEqual$1, isArray, isString } from 'lodash-es';
|
|
14
14
|
import { transform, isEqual } from 'lodash';
|
|
15
15
|
import { AXPSessionService, AXPAuthGuard, AXPPermissionDefinitionsDataSourceDefinition } from '@acorex/platform/auth';
|
|
16
16
|
import { Router, ActivatedRoute, RouterModule, ROUTES } from '@angular/router';
|
|
17
|
-
import { defineCommand, AXP_COMMAND_DEFINITION_CATEGORY_ENTITY, AXPCommandService, AXPQueryService, AXPQueryExecutor, provideCommandSetups, provideQuerySetups, AXPCommandRegistry, AXPQueryRegistry } from '@acorex/platform/runtime';
|
|
17
|
+
import { defineCommand, AXP_COMMAND_DEFINITION_CATEGORY_ENTITY, AXPCommandService, AXPQueryService, AXPQueryExecutor, AXPCommandExecutor, provideCommandSetups, provideQuerySetups, AXPCommandRegistry, AXPQueryRegistry } from '@acorex/platform/runtime';
|
|
18
18
|
import * as i1 from '@acorex/components/button';
|
|
19
19
|
import { AXButtonModule } from '@acorex/components/button';
|
|
20
20
|
import * as i4 from '@acorex/components/skeleton';
|
|
@@ -24,7 +24,7 @@ import { AXPopoverModule } from '@acorex/components/popover';
|
|
|
24
24
|
import { AXFormatService } from '@acorex/core/format';
|
|
25
25
|
import * as i5 from '@angular/common';
|
|
26
26
|
import { CommonModule, AsyncPipe } from '@angular/common';
|
|
27
|
-
import { AXPThemeLayoutBlockComponent, AXPPreloadFiltersComponent, AXPStateMessageComponent, AXPColumnItemListComponent, AXPDataSelectorService, AXPPageComponentRegistryService } from '@acorex/platform/layout/components';
|
|
27
|
+
import { AXPThemeLayoutBlockComponent, AXPPreloadFiltersComponent, AXP_PAGE_COMPONENT_PROVIDER, AXPStateMessageComponent, AXPColumnItemListComponent, AXPDataSelectorService, AXPPageComponentRegistryService } from '@acorex/platform/layout/components';
|
|
28
28
|
import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageComponentInstanceRegistryService } from '@acorex/platform/layout/views';
|
|
29
29
|
import { AXDataSource } from '@acorex/cdk/common';
|
|
30
30
|
import * as i1$3 from '@acorex/platform/workflow';
|
|
@@ -57,19 +57,22 @@ import { AXLoadingModule } from '@acorex/components/loading';
|
|
|
57
57
|
import * as i6$1 from '@acorex/components/tag-box';
|
|
58
58
|
import { AXTagBoxModule } from '@acorex/components/tag-box';
|
|
59
59
|
import { AXValidationModule } from '@acorex/core/validation';
|
|
60
|
-
import { AXP_DISABLED_PROPERTY, AXP_ALLOW_CLEAR_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_NAME_PROPERTY, AXP_READONLY_PROPERTY, AXP_PLACEHOLDER_PROPERTY, AXP_BEHAVIOR_PROPERTY_GROUP, AXPProviderSelectWidgetEditBase, AXP_ROW_EXPR_PREFIX,
|
|
60
|
+
import { AXP_DISABLED_PROPERTY, AXP_ALLOW_CLEAR_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_NAME_PROPERTY, AXP_READONLY_PROPERTY, AXP_PLACEHOLDER_PROPERTY, AXP_BEHAVIOR_PROPERTY_GROUP, AXPProviderSelectWidgetEditBase, AXP_ROW_EXPR_PREFIX, AXP_DOWNLOADABLE_PROPERTY, AXP_DESCRIPTION_PROPERTY } from '@acorex/platform/layout/widgets';
|
|
61
61
|
import * as i2$2 from '@acorex/components/select-box';
|
|
62
62
|
import { AXSelectBoxModule } from '@acorex/components/select-box';
|
|
63
63
|
import * as i4$2 from '@acorex/components/dropdown';
|
|
64
64
|
import { AXDropdownModule } from '@acorex/components/dropdown';
|
|
65
65
|
import * as i2$3 from '@acorex/components/tabs';
|
|
66
66
|
import { AXTabsModule } from '@acorex/components/tabs';
|
|
67
|
-
import * as i4$3 from '@acorex/components/collapse';
|
|
68
|
-
import { AXCollapseModule } from '@acorex/components/collapse';
|
|
69
67
|
import * as i5$2 from '@acorex/components/label';
|
|
70
68
|
import { AXLabelModule } from '@acorex/components/label';
|
|
71
69
|
import * as i7 from '@acorex/components/text-box';
|
|
72
70
|
import { AXTextBoxModule } from '@acorex/components/text-box';
|
|
71
|
+
import { AXFileService } from '@acorex/core/file';
|
|
72
|
+
import { AXUploaderZoneDirective } from '@acorex/cdk/uploader';
|
|
73
|
+
import { AXUploaderModule } from '@acorex/components/uploader';
|
|
74
|
+
import * as i4$3 from '@acorex/components/collapse';
|
|
75
|
+
import { AXCollapseModule } from '@acorex/components/collapse';
|
|
73
76
|
|
|
74
77
|
function ensureListActions(ctx) {
|
|
75
78
|
ctx.interfaces.update((i) => {
|
|
@@ -116,6 +119,18 @@ function ensureLayoutPropertyView(layout, prop) {
|
|
|
116
119
|
|
|
117
120
|
const AXP_ENTITY_ACTION_PLUGIN = new InjectionToken('AXP_ENTITY_ACTION_PLUGIN');
|
|
118
121
|
|
|
122
|
+
function ensureMasterListView(entity) {
|
|
123
|
+
entity.interfaces ??= {};
|
|
124
|
+
entity.interfaces.master ??= {};
|
|
125
|
+
if (!entity.interfaces.master.list) {
|
|
126
|
+
entity.interfaces.master.list = { views: [] };
|
|
127
|
+
}
|
|
128
|
+
return entity.interfaces.master.list;
|
|
129
|
+
}
|
|
130
|
+
function ensureMasterListLayouts(list) {
|
|
131
|
+
list.layouts ??= {};
|
|
132
|
+
return list.layouts;
|
|
133
|
+
}
|
|
119
134
|
function createModifierContext(entity) {
|
|
120
135
|
const ctx = {
|
|
121
136
|
entity,
|
|
@@ -293,6 +308,27 @@ function createModifierContext(entity) {
|
|
|
293
308
|
entity.interfaces.master.list = updater(entity.interfaces.master.list);
|
|
294
309
|
return ctx;
|
|
295
310
|
},
|
|
311
|
+
card: {
|
|
312
|
+
get: () => entity.interfaces?.master?.list?.layouts?.card,
|
|
313
|
+
set: (layout) => {
|
|
314
|
+
const list = ensureMasterListView(entity);
|
|
315
|
+
ensureMasterListLayouts(list).card = layout;
|
|
316
|
+
return ctx;
|
|
317
|
+
},
|
|
318
|
+
update: (updater) => {
|
|
319
|
+
const list = ensureMasterListView(entity);
|
|
320
|
+
const layouts = ensureMasterListLayouts(list);
|
|
321
|
+
layouts.card = updater(layouts.card);
|
|
322
|
+
return ctx;
|
|
323
|
+
},
|
|
324
|
+
remove: () => {
|
|
325
|
+
const layouts = entity.interfaces?.master?.list?.layouts;
|
|
326
|
+
if (layouts?.card) {
|
|
327
|
+
delete layouts.card;
|
|
328
|
+
}
|
|
329
|
+
return ctx;
|
|
330
|
+
},
|
|
331
|
+
},
|
|
296
332
|
},
|
|
297
333
|
create: {
|
|
298
334
|
get: () => entity.interfaces?.master?.create,
|
|
@@ -1173,7 +1209,7 @@ function isTruthyVisible(value) {
|
|
|
1173
1209
|
* Resolves `AXPRelatedEntity.columns` for `entity-list`:
|
|
1174
1210
|
* - `string[]` → `includeColumns` only (legacy).
|
|
1175
1211
|
* - `AXPEntityTableColumn[]` → evaluates `options.visible` when it is a string (parent detail context),
|
|
1176
|
-
* drops
|
|
1212
|
+
* drops invisible columns, and returns `relatedTableColumns` + matching `includeColumns`.
|
|
1177
1213
|
*/
|
|
1178
1214
|
async function resolveRelatedEntityColumns(columns, expressionEvaluator, scope) {
|
|
1179
1215
|
if (!columns?.length) {
|
|
@@ -1185,9 +1221,6 @@ async function resolveRelatedEntityColumns(columns, expressionEvaluator, scope)
|
|
|
1185
1221
|
const relatedTableColumns = [];
|
|
1186
1222
|
for (const raw of columns) {
|
|
1187
1223
|
const col = cloneDeep(raw);
|
|
1188
|
-
if (col.hidden === true) {
|
|
1189
|
-
continue;
|
|
1190
|
-
}
|
|
1191
1224
|
let visible = col.options?.visible;
|
|
1192
1225
|
if (visible === undefined) {
|
|
1193
1226
|
visible = true;
|
|
@@ -4112,6 +4145,47 @@ class AXPEntityListViewColumnViewModel {
|
|
|
4112
4145
|
}, ...(ngDevMode ? [{ debugName: "node" }] : /* istanbul ignore next */ []));
|
|
4113
4146
|
}
|
|
4114
4147
|
}
|
|
4148
|
+
class AXPEntityListViewCardFieldViewModel {
|
|
4149
|
+
constructor(property, field) {
|
|
4150
|
+
this.property = property;
|
|
4151
|
+
this.field = field;
|
|
4152
|
+
this.name = this.property.name;
|
|
4153
|
+
this.title = this.field.title ?? this.property.title;
|
|
4154
|
+
this.visible = this.field?.options?.visible ?? true;
|
|
4155
|
+
this.layout = this.field.layout;
|
|
4156
|
+
this.display = isCardFieldBadgeDisplay(this.field.display) ? 'badge' : 'simple';
|
|
4157
|
+
this.isBadgeDisplay = isCardFieldBadgeDisplay(this.field.display);
|
|
4158
|
+
this.badgeColor = resolveCardFieldBadgeColor(this.field);
|
|
4159
|
+
this.isStatusWidget = this.property.schema?.interface?.type === 'status-widget';
|
|
4160
|
+
this.node = computed(() => {
|
|
4161
|
+
const widget = this.property.schema.interface;
|
|
4162
|
+
return {
|
|
4163
|
+
path: this.field.options?.dataPath ?? this.name,
|
|
4164
|
+
type: widget.type,
|
|
4165
|
+
options: {
|
|
4166
|
+
...widget?.options,
|
|
4167
|
+
...this.field?.options,
|
|
4168
|
+
columnName: this.name,
|
|
4169
|
+
...(widget.children ? { children: widget.children } : {}),
|
|
4170
|
+
},
|
|
4171
|
+
};
|
|
4172
|
+
}, ...(ngDevMode ? [{ debugName: "node" }] : /* istanbul ignore next */ []));
|
|
4173
|
+
/** Readonly column node for status fields shown as header badges. */
|
|
4174
|
+
this.headerBadgeNode = computed(() => {
|
|
4175
|
+
const base = this.node();
|
|
4176
|
+
if (!this.isStatusWidget) {
|
|
4177
|
+
return base;
|
|
4178
|
+
}
|
|
4179
|
+
return {
|
|
4180
|
+
...base,
|
|
4181
|
+
options: {
|
|
4182
|
+
...base.options,
|
|
4183
|
+
readonly: true,
|
|
4184
|
+
},
|
|
4185
|
+
};
|
|
4186
|
+
}, ...(ngDevMode ? [{ debugName: "headerBadgeNode" }] : /* istanbul ignore next */ []));
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4115
4189
|
|
|
4116
4190
|
class AXPEntityDetailListViewModel {
|
|
4117
4191
|
constructor(injector, detailEntityConfig, parent) {
|
|
@@ -4816,6 +4890,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
4816
4890
|
args: [{ providedIn: 'root' }]
|
|
4817
4891
|
}] });
|
|
4818
4892
|
|
|
4893
|
+
/** Built-in card list row action that toggles multi-select mode via {@link AXPEntityMasterListViewModel.selectedItems}. */
|
|
4894
|
+
const AXPEntityMasterListCardSelectActionName = 'axp-entity-card:select';
|
|
4819
4895
|
class AXPEntityMasterListViewQueryViewModel {
|
|
4820
4896
|
constructor(entity, section, view) {
|
|
4821
4897
|
this.entity = entity;
|
|
@@ -4831,6 +4907,83 @@ class AXPEntityMasterListViewQueryViewModel {
|
|
|
4831
4907
|
}
|
|
4832
4908
|
}
|
|
4833
4909
|
class AXPEntityMasterListViewModel {
|
|
4910
|
+
/**
|
|
4911
|
+
* Sets global card body visibility for card layout.
|
|
4912
|
+
*/
|
|
4913
|
+
setCardContentExpanded(expanded) {
|
|
4914
|
+
if (this.activeListLayout() !== 'card' || this.cardContentExpanded() === expanded) {
|
|
4915
|
+
return;
|
|
4916
|
+
}
|
|
4917
|
+
this.cardContentExpanded.set(expanded);
|
|
4918
|
+
void this.saveSettings('cardContentExpanded', expanded);
|
|
4919
|
+
}
|
|
4920
|
+
/**
|
|
4921
|
+
* Switches table vs compact/expanded card presentation.
|
|
4922
|
+
*/
|
|
4923
|
+
setListDisplayMode(mode) {
|
|
4924
|
+
if (mode === 'table') {
|
|
4925
|
+
if (this.enabledListLayouts().includes('table')) {
|
|
4926
|
+
this.setListLayout('table');
|
|
4927
|
+
}
|
|
4928
|
+
return;
|
|
4929
|
+
}
|
|
4930
|
+
if (!this.enabledListLayouts().includes('card')) {
|
|
4931
|
+
return;
|
|
4932
|
+
}
|
|
4933
|
+
const expanded = mode === 'card-expanded';
|
|
4934
|
+
if (this.activeListLayout() !== 'card') {
|
|
4935
|
+
this.setListLayout('card');
|
|
4936
|
+
}
|
|
4937
|
+
if (this.cardContentExpanded() !== expanded) {
|
|
4938
|
+
this.cardContentExpanded.set(expanded);
|
|
4939
|
+
void this.saveSettings('cardContentExpanded', expanded);
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
static { this.LAYOUT_META = {
|
|
4943
|
+
table: { title: '@general:terms.interface.table-view', icon: 'fa-table' },
|
|
4944
|
+
card: { title: '@general:terms.interface.card-view', icon: 'fa-grid-2' },
|
|
4945
|
+
}; }
|
|
4946
|
+
/**
|
|
4947
|
+
* Switches to the given layout when it is enabled.
|
|
4948
|
+
* Persists the choice at entity list level (shared across all views).
|
|
4949
|
+
*/
|
|
4950
|
+
setListLayout(layout) {
|
|
4951
|
+
if (!this.enabledListLayouts().includes(layout) || this.activeListLayout() === layout) {
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4954
|
+
this.activeListLayout.set(layout);
|
|
4955
|
+
void this.saveSettings('listLayout', layout);
|
|
4956
|
+
}
|
|
4957
|
+
/**
|
|
4958
|
+
* Cycles to the next enabled layout (table → card → table …).
|
|
4959
|
+
* No-op when only one layout is enabled.
|
|
4960
|
+
*/
|
|
4961
|
+
switchListLayout() {
|
|
4962
|
+
const enabled = this.enabledListLayouts();
|
|
4963
|
+
if (enabled.length <= 1) {
|
|
4964
|
+
return this.activeListLayout();
|
|
4965
|
+
}
|
|
4966
|
+
const currentIndex = enabled.indexOf(this.activeListLayout());
|
|
4967
|
+
const nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % enabled.length;
|
|
4968
|
+
const next = enabled[nextIndex];
|
|
4969
|
+
this.setListLayout(next);
|
|
4970
|
+
return next;
|
|
4971
|
+
}
|
|
4972
|
+
resolveActiveListLayout(preferred) {
|
|
4973
|
+
const enabled = this.enabledListLayouts();
|
|
4974
|
+
const layout = preferred && enabled.includes(preferred) ? preferred : enabled[0];
|
|
4975
|
+
this.activeListLayout.set(layout);
|
|
4976
|
+
}
|
|
4977
|
+
applyListLayoutFromSettings(entitySetting) {
|
|
4978
|
+
const savedLayout = entitySetting.list?.layout;
|
|
4979
|
+
if (savedLayout) {
|
|
4980
|
+
this.resolveActiveListLayout(savedLayout);
|
|
4981
|
+
}
|
|
4982
|
+
if (typeof entitySetting.list?.cardContentExpanded === 'boolean') {
|
|
4983
|
+
this.cardContentExpanded.set(entitySetting.list.cardContentExpanded);
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
//#endregion
|
|
4834
4987
|
createExpressionScope(data) {
|
|
4835
4988
|
return {
|
|
4836
4989
|
context: {
|
|
@@ -4860,6 +5013,62 @@ class AXPEntityMasterListViewModel {
|
|
|
4860
5013
|
const resolved = result?.value;
|
|
4861
5014
|
return typeof resolved === 'string' && resolved.trim() !== '' ? resolved : title;
|
|
4862
5015
|
}
|
|
5016
|
+
/**
|
|
5017
|
+
* Resolves a card header value (title, description, icon) from a property path, expression, or literal.
|
|
5018
|
+
* Supports multiple `{{ ... }}` segments in one string (e.g. `{{context.eval("a")}} - {{context.eval("b")}}`).
|
|
5019
|
+
* Multi-language maps (e.g. `{ "en-US": "...", "fa-IR": "..." }`) are returned as-is for the translate pipe.
|
|
5020
|
+
*/
|
|
5021
|
+
async resolveCardHeaderValue(value, rowData) {
|
|
5022
|
+
if (!value) {
|
|
5023
|
+
return '';
|
|
5024
|
+
}
|
|
5025
|
+
if (value.includes('{{')) {
|
|
5026
|
+
const scope = this.createExpressionScope(rowData);
|
|
5027
|
+
const result = await this.expressionEvaluator.evaluate(value, scope);
|
|
5028
|
+
if (result === false) {
|
|
5029
|
+
return '';
|
|
5030
|
+
}
|
|
5031
|
+
return this.normalizeCardHeaderDisplayValue(result);
|
|
5032
|
+
}
|
|
5033
|
+
const resolved = get(rowData, value);
|
|
5034
|
+
return this.normalizeCardHeaderDisplayValue(resolved);
|
|
5035
|
+
}
|
|
5036
|
+
/**
|
|
5037
|
+
* Keeps i18n payloads intact for `translate`; coerces only plain scalars to string.
|
|
5038
|
+
*/
|
|
5039
|
+
normalizeCardHeaderDisplayValue(value) {
|
|
5040
|
+
if (value == null || value === false) {
|
|
5041
|
+
return '';
|
|
5042
|
+
}
|
|
5043
|
+
if (typeof value === 'string') {
|
|
5044
|
+
return value;
|
|
5045
|
+
}
|
|
5046
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
5047
|
+
return String(value);
|
|
5048
|
+
}
|
|
5049
|
+
if (typeof value === 'object' && !Array.isArray(value) && this.translation.isValidMultiLanguageObject(value)) {
|
|
5050
|
+
return value;
|
|
5051
|
+
}
|
|
5052
|
+
return '';
|
|
5053
|
+
}
|
|
5054
|
+
/**
|
|
5055
|
+
* Resolves badge color for a card header field (semantic token or expression per row).
|
|
5056
|
+
*/
|
|
5057
|
+
async resolveCardBadgeColor(field, rowData) {
|
|
5058
|
+
return this.resolveCardBadgeColorValue(field.badgeColor, rowData);
|
|
5059
|
+
}
|
|
5060
|
+
async resolveCardBadgeColorValue(color, rowData) {
|
|
5061
|
+
if (color == null || color === '') {
|
|
5062
|
+
return 'default';
|
|
5063
|
+
}
|
|
5064
|
+
const scope = this.createExpressionScope(rowData);
|
|
5065
|
+
const result = await this.expressionEvaluator.evaluate(color, scope);
|
|
5066
|
+
if (result === false || result == null) {
|
|
5067
|
+
return 'default';
|
|
5068
|
+
}
|
|
5069
|
+
const resolved = String(result).trim();
|
|
5070
|
+
return resolved !== '' ? resolved : 'default';
|
|
5071
|
+
}
|
|
4863
5072
|
async resolveTriggerName(triggerName, data) {
|
|
4864
5073
|
if (!triggerName) {
|
|
4865
5074
|
return triggerName;
|
|
@@ -4883,6 +5092,7 @@ class AXPEntityMasterListViewModel {
|
|
|
4883
5092
|
this.view.set(this.views().find((c) => c.name == (viewName || selectedViewName)) ?? this.views()[0]);
|
|
4884
5093
|
this.applyViewSorts();
|
|
4885
5094
|
this.applyViewColumns();
|
|
5095
|
+
this.applyCardFields();
|
|
4886
5096
|
this.applyViewFilters();
|
|
4887
5097
|
this.resolvedListPaging.set(null);
|
|
4888
5098
|
await this.applySettings();
|
|
@@ -4899,10 +5109,12 @@ class AXPEntityMasterListViewModel {
|
|
|
4899
5109
|
this.settings = this.injector.get(AXPSettingsService);
|
|
4900
5110
|
this.widgetResolver = this.injector.get(AXPWidgetRegistryService);
|
|
4901
5111
|
this.expressionEvaluator = this.injector.get(AXPExpressionEvaluatorService);
|
|
5112
|
+
this.translation = this.injector.get(AXTranslationService);
|
|
4902
5113
|
this.commandService = this.injector.get(AXPCommandService);
|
|
4903
5114
|
this.eventService = this.injector.get(AXPBroadcastEventService);
|
|
4904
5115
|
this.queryExecutor = this.injector.get(AXPQueryExecutor);
|
|
4905
5116
|
this.filterOperatorMiddleware = this.injector.get(AXPFilterOperatorMiddlewareService);
|
|
5117
|
+
this.entityDefinitionRegistry = this.injector.get(AXPEntityDefinitionRegistryService);
|
|
4906
5118
|
this.settingEntityKey = `${this.config.module}:${this.config.name}`;
|
|
4907
5119
|
this.destroyed = new Subject();
|
|
4908
5120
|
this.lastAppliedSortKey = null;
|
|
@@ -4938,6 +5150,32 @@ class AXPEntityMasterListViewModel {
|
|
|
4938
5150
|
}
|
|
4939
5151
|
return this.showRowIndexColumnEnabled();
|
|
4940
5152
|
}, ...(ngDevMode ? [{ debugName: "showIndexColumn" }] : /* istanbul ignore next */ []));
|
|
5153
|
+
//#region ---- List Layouts (table / card) ----
|
|
5154
|
+
/** Layouts enabled via `interfaces.master.list.layouts.*.enabled`. */
|
|
5155
|
+
this.enabledListLayouts = computed(() => resolveEnabledMasterListLayouts(this._viewDef?.layouts), ...(ngDevMode ? [{ debugName: "enabledListLayouts" }] : /* istanbul ignore next */ []));
|
|
5156
|
+
/** True when the user can switch between multiple enabled layouts. */
|
|
5157
|
+
this.canSwitchListLayout = computed(() => this.enabledListLayouts().length > 1, ...(ngDevMode ? [{ debugName: "canSwitchListLayout" }] : /* istanbul ignore next */ []));
|
|
5158
|
+
/** Active list layout for the current view. */
|
|
5159
|
+
this.activeListLayout = signal(resolveDefaultMasterListLayout(this._viewDef?.layouts), ...(ngDevMode ? [{ debugName: "activeListLayout" }] : /* istanbul ignore next */ []));
|
|
5160
|
+
this.tableLayout = computed(() => this._viewDef?.layouts?.table, ...(ngDevMode ? [{ debugName: "tableLayout" }] : /* istanbul ignore next */ []));
|
|
5161
|
+
this.cardLayout = computed(() => this._viewDef?.layouts?.card, ...(ngDevMode ? [{ debugName: "cardLayout" }] : /* istanbul ignore next */ []));
|
|
5162
|
+
/** Global expanded state for all card bodies in card layout. */
|
|
5163
|
+
this.cardContentExpanded = signal(true, ...(ngDevMode ? [{ debugName: "cardContentExpanded" }] : /* istanbul ignore next */ []));
|
|
5164
|
+
/** Current presentation mode (table, compact cards, or expanded cards). */
|
|
5165
|
+
this.listDisplayMode = computed(() => {
|
|
5166
|
+
if (this.activeListLayout() === 'table') {
|
|
5167
|
+
return 'table';
|
|
5168
|
+
}
|
|
5169
|
+
return this.cardContentExpanded() ? 'card-expanded' : 'card-compact';
|
|
5170
|
+
}, ...(ngDevMode ? [{ debugName: "listDisplayMode" }] : /* istanbul ignore next */ []));
|
|
5171
|
+
/** Enabled layouts with display metadata for the title layout switcher. */
|
|
5172
|
+
this.availableListLayouts = computed(() => this.enabledListLayouts().map((id) => ({
|
|
5173
|
+
id,
|
|
5174
|
+
title: AXPEntityMasterListViewModel.LAYOUT_META[id].title,
|
|
5175
|
+
icon: AXPEntityMasterListViewModel.LAYOUT_META[id].icon,
|
|
5176
|
+
})), ...(ngDevMode ? [{ debugName: "availableListLayouts" }] : /* istanbul ignore next */ []));
|
|
5177
|
+
/** Active layout option (for title icon and switcher selection state). */
|
|
5178
|
+
this.currentListLayout = computed(() => this.availableListLayouts().find((layout) => layout.id === this.activeListLayout()) ?? null, ...(ngDevMode ? [{ debugName: "currentListLayout" }] : /* istanbul ignore next */ []));
|
|
4941
5179
|
this.dataSource = new AXDataSource({
|
|
4942
5180
|
byKey: async (key) => {
|
|
4943
5181
|
const execute = this.entityDef.queries?.byKey?.execute;
|
|
@@ -5031,7 +5269,7 @@ class AXPEntityMasterListViewModel {
|
|
|
5031
5269
|
// const visibleProperties = properties.filter(({ schema }) => schema?.visible !== false);
|
|
5032
5270
|
const propNames = new Set(properties.map(({ name }) => name));
|
|
5033
5271
|
return columns
|
|
5034
|
-
.filter(({ name, showAs,
|
|
5272
|
+
.filter(({ name, showAs, options }) => (propNames.has(name) || showAs) && options?.visible !== false)
|
|
5035
5273
|
.map((column) => {
|
|
5036
5274
|
if (column.showAs) {
|
|
5037
5275
|
const widgetConfig = this.widgetResolver.resolve(column.showAs.type);
|
|
@@ -5060,6 +5298,13 @@ class AXPEntityMasterListViewModel {
|
|
|
5060
5298
|
return this.columns().filter((c) => c.visible == true).length;
|
|
5061
5299
|
};
|
|
5062
5300
|
this.columns = signal([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
5301
|
+
this.allAvailableCardFields = () => {
|
|
5302
|
+
const fields = this.cardLayout()?.fields ?? [];
|
|
5303
|
+
return fields
|
|
5304
|
+
.map((field) => this.resolveCardField(field))
|
|
5305
|
+
.filter((field) => field != null);
|
|
5306
|
+
};
|
|
5307
|
+
this.cardFields = signal([], ...(ngDevMode ? [{ debugName: "cardFields" }] : /* istanbul ignore next */ []));
|
|
5063
5308
|
//****************** Sort ******************//
|
|
5064
5309
|
this.sortableFields = () => {
|
|
5065
5310
|
const props = this.entityDef.properties.filter((c) => c.options?.sort?.enabled);
|
|
@@ -5182,12 +5427,17 @@ class AXPEntityMasterListViewModel {
|
|
|
5182
5427
|
this.saveSettings('view');
|
|
5183
5428
|
}
|
|
5184
5429
|
if (!(await this.shouldLoadPersistedListState())) {
|
|
5430
|
+
const entitySetting = await this.settings.get(this.settingEntityKey);
|
|
5431
|
+
if (entitySetting) {
|
|
5432
|
+
this.applyListLayoutFromSettings(entitySetting);
|
|
5433
|
+
}
|
|
5185
5434
|
this.loadListPagingFromViewSettings(null);
|
|
5186
5435
|
this.expandedRowIds.set([]);
|
|
5187
5436
|
return;
|
|
5188
5437
|
}
|
|
5189
5438
|
const listViewSetting = await this.settings.get(this.settingEntityKey);
|
|
5190
5439
|
if (listViewSetting) {
|
|
5440
|
+
this.applyListLayoutFromSettings(listViewSetting);
|
|
5191
5441
|
const columns = listViewSetting.list?.views?.[this.view().name]?.columns;
|
|
5192
5442
|
const sorts = listViewSetting.list?.views?.[this.view().name]?.sorts;
|
|
5193
5443
|
const filters = listViewSetting.list?.views?.[this.view().name]?.filters;
|
|
@@ -5438,6 +5688,24 @@ class AXPEntityMasterListViewModel {
|
|
|
5438
5688
|
},
|
|
5439
5689
|
}));
|
|
5440
5690
|
break;
|
|
5691
|
+
case 'listLayout':
|
|
5692
|
+
updateSettings((prev) => ({
|
|
5693
|
+
...prev,
|
|
5694
|
+
list: {
|
|
5695
|
+
...prev?.list,
|
|
5696
|
+
layout: data,
|
|
5697
|
+
},
|
|
5698
|
+
}));
|
|
5699
|
+
break;
|
|
5700
|
+
case 'cardContentExpanded':
|
|
5701
|
+
updateSettings((prev) => ({
|
|
5702
|
+
...prev,
|
|
5703
|
+
list: {
|
|
5704
|
+
...prev?.list,
|
|
5705
|
+
cardContentExpanded: data,
|
|
5706
|
+
},
|
|
5707
|
+
}));
|
|
5708
|
+
break;
|
|
5441
5709
|
default:
|
|
5442
5710
|
break;
|
|
5443
5711
|
}
|
|
@@ -5501,6 +5769,32 @@ class AXPEntityMasterListViewModel {
|
|
|
5501
5769
|
}));
|
|
5502
5770
|
return actions.filter(Boolean);
|
|
5503
5771
|
}
|
|
5772
|
+
/**
|
|
5773
|
+
* Secondary actions for card layout rows, with the built-in Select action last (divider above it).
|
|
5774
|
+
*/
|
|
5775
|
+
async cardSecondaryRowActions(rowData) {
|
|
5776
|
+
const actions = await this.secondaryRowActions(rowData);
|
|
5777
|
+
const items = actions
|
|
5778
|
+
.filter((a) => a.name !== AXPEntityMasterListCardSelectActionName)
|
|
5779
|
+
.map((a) => ({
|
|
5780
|
+
name: a.name,
|
|
5781
|
+
title: a.title,
|
|
5782
|
+
icon: a.icon,
|
|
5783
|
+
color: a.color,
|
|
5784
|
+
disabled: a.disabled,
|
|
5785
|
+
divided: a.separated,
|
|
5786
|
+
}));
|
|
5787
|
+
items.push({
|
|
5788
|
+
name: AXPEntityMasterListCardSelectActionName,
|
|
5789
|
+
title: '@general:actions.select.title',
|
|
5790
|
+
icon: 'fa-square-check',
|
|
5791
|
+
color: 'default',
|
|
5792
|
+
disabled: false,
|
|
5793
|
+
divided: items.length > 0,
|
|
5794
|
+
isCardSelect: true,
|
|
5795
|
+
});
|
|
5796
|
+
return items;
|
|
5797
|
+
}
|
|
5504
5798
|
static { this.URL_FILTER_OPERATOR_TYPES = new Set([
|
|
5505
5799
|
'equal',
|
|
5506
5800
|
'isNull',
|
|
@@ -5725,6 +6019,141 @@ class AXPEntityMasterListViewModel {
|
|
|
5725
6019
|
this.columns.set(columns);
|
|
5726
6020
|
this.saveSettings('columnOrders', columns);
|
|
5727
6021
|
}
|
|
6022
|
+
//****************** Card fields ******************//
|
|
6023
|
+
mergeDisplayFieldWithColumn(field, column) {
|
|
6024
|
+
if (!column) {
|
|
6025
|
+
return field;
|
|
6026
|
+
}
|
|
6027
|
+
return {
|
|
6028
|
+
...field,
|
|
6029
|
+
title: field.title ?? column.title,
|
|
6030
|
+
showAs: field.showAs ?? column.showAs,
|
|
6031
|
+
options: { ...column.options, ...field.options },
|
|
6032
|
+
};
|
|
6033
|
+
}
|
|
6034
|
+
buildVirtualDisplayProperty(name, title, showAs) {
|
|
6035
|
+
const widgetConfig = this.widgetResolver.resolve(showAs.type);
|
|
6036
|
+
const { title: _wcTitle, description: _wcDescription, ...widgetRest } = widgetConfig;
|
|
6037
|
+
return {
|
|
6038
|
+
...widgetRest,
|
|
6039
|
+
name,
|
|
6040
|
+
title: title ?? '',
|
|
6041
|
+
schema: {
|
|
6042
|
+
dataType: 'string',
|
|
6043
|
+
interface: {
|
|
6044
|
+
type: showAs.type,
|
|
6045
|
+
options: showAs.options,
|
|
6046
|
+
},
|
|
6047
|
+
},
|
|
6048
|
+
};
|
|
6049
|
+
}
|
|
6050
|
+
normalizeRelatedColumnNames(columns) {
|
|
6051
|
+
if (!columns?.length) {
|
|
6052
|
+
return [];
|
|
6053
|
+
}
|
|
6054
|
+
return columns.map((column) => (typeof column === 'string' ? column : column.name));
|
|
6055
|
+
}
|
|
6056
|
+
findRelatedEntityForField(fieldName) {
|
|
6057
|
+
for (const related of this.entityDef.relatedEntities ?? []) {
|
|
6058
|
+
const columnNames = this.normalizeRelatedColumnNames(related.columns);
|
|
6059
|
+
if (columnNames.includes(fieldName)) {
|
|
6060
|
+
return { entity: related.entity };
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
return null;
|
|
6064
|
+
}
|
|
6065
|
+
getRegisteredEntity(fullName) {
|
|
6066
|
+
return this.entityDefinitionRegistry
|
|
6067
|
+
.getAll()
|
|
6068
|
+
.find((entity) => `${entity.module}.${entity.name}` === fullName);
|
|
6069
|
+
}
|
|
6070
|
+
resolveRelatedEntityProperty(fieldName) {
|
|
6071
|
+
const relatedRef = this.findRelatedEntityForField(fieldName);
|
|
6072
|
+
if (!relatedRef) {
|
|
6073
|
+
return null;
|
|
6074
|
+
}
|
|
6075
|
+
const relatedDef = this.getRegisteredEntity(relatedRef.entity);
|
|
6076
|
+
return relatedDef?.properties?.find((property) => property.name === fieldName) ?? null;
|
|
6077
|
+
}
|
|
6078
|
+
resolveRelatedEntityColumnDataPath(relatedEntityKey, fieldName) {
|
|
6079
|
+
const relatedDef = this.getRegisteredEntity(relatedEntityKey);
|
|
6080
|
+
const column = relatedDef?.columns?.find(({ name }) => name === fieldName);
|
|
6081
|
+
return column?.options?.dataPath;
|
|
6082
|
+
}
|
|
6083
|
+
deriveDataPathFromLookupProperty(property) {
|
|
6084
|
+
const iface = property.schema?.interface;
|
|
6085
|
+
if (iface?.type !== 'lookup-editor') {
|
|
6086
|
+
return undefined;
|
|
6087
|
+
}
|
|
6088
|
+
const textField = iface.options?.['textField'] ?? 'title';
|
|
6089
|
+
const expose = iface.options?.['expose'];
|
|
6090
|
+
return expose?.find((entry) => entry.source === textField)?.target;
|
|
6091
|
+
}
|
|
6092
|
+
resolveParentDataPathForRelatedField(fieldName, relatedEntityKey, relatedProperty) {
|
|
6093
|
+
const parentColumn = this.entityDef.columns?.find(({ name }) => name === fieldName);
|
|
6094
|
+
if (parentColumn?.options?.dataPath) {
|
|
6095
|
+
return parentColumn.options.dataPath;
|
|
6096
|
+
}
|
|
6097
|
+
const relatedDataPath = this.resolveRelatedEntityColumnDataPath(relatedEntityKey, fieldName) ??
|
|
6098
|
+
this.deriveDataPathFromLookupProperty(relatedProperty);
|
|
6099
|
+
if (!relatedDataPath) {
|
|
6100
|
+
return undefined;
|
|
6101
|
+
}
|
|
6102
|
+
const relatedLeaf = relatedDataPath.split('.').pop();
|
|
6103
|
+
for (const property of this.entityDef.properties ?? []) {
|
|
6104
|
+
const expose = property.schema?.interface?.options?.['expose'];
|
|
6105
|
+
if (!expose) {
|
|
6106
|
+
continue;
|
|
6107
|
+
}
|
|
6108
|
+
const match = expose.find((entry) => entry.target === relatedDataPath ||
|
|
6109
|
+
entry.target.endsWith(`.${relatedDataPath}`) ||
|
|
6110
|
+
(relatedLeaf != null && entry.source === relatedLeaf));
|
|
6111
|
+
if (match) {
|
|
6112
|
+
return match.target;
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
return relatedDataPath;
|
|
6116
|
+
}
|
|
6117
|
+
resolveDisplayFieldProperty(field, column) {
|
|
6118
|
+
const showAs = field.showAs ?? column?.showAs;
|
|
6119
|
+
if (showAs) {
|
|
6120
|
+
return this.buildVirtualDisplayProperty(field.name, field.title ?? column?.title, showAs);
|
|
6121
|
+
}
|
|
6122
|
+
const property = this.entityDef.properties.find(({ name }) => name === field.name);
|
|
6123
|
+
if (property) {
|
|
6124
|
+
return property;
|
|
6125
|
+
}
|
|
6126
|
+
return this.resolveRelatedEntityProperty(field.name);
|
|
6127
|
+
}
|
|
6128
|
+
resolveCardField(field) {
|
|
6129
|
+
const column = this.entityDef.columns?.find(({ name }) => name === field.name);
|
|
6130
|
+
const mergedField = this.mergeDisplayFieldWithColumn(field, column);
|
|
6131
|
+
if (field.options?.visible === false) {
|
|
6132
|
+
return null;
|
|
6133
|
+
}
|
|
6134
|
+
const property = this.resolveDisplayFieldProperty(mergedField, column);
|
|
6135
|
+
if (!property) {
|
|
6136
|
+
return null;
|
|
6137
|
+
}
|
|
6138
|
+
const isOwnProperty = this.entityDef.properties.some(({ name }) => name === field.name);
|
|
6139
|
+
let resolvedField = mergedField;
|
|
6140
|
+
if (!isOwnProperty && !resolvedField.options?.dataPath) {
|
|
6141
|
+
const relatedRef = this.findRelatedEntityForField(field.name);
|
|
6142
|
+
if (relatedRef) {
|
|
6143
|
+
const dataPath = this.resolveParentDataPathForRelatedField(field.name, relatedRef.entity, property);
|
|
6144
|
+
if (dataPath) {
|
|
6145
|
+
resolvedField = {
|
|
6146
|
+
...resolvedField,
|
|
6147
|
+
options: { ...resolvedField.options, dataPath },
|
|
6148
|
+
};
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
return new AXPEntityListViewCardFieldViewModel(property, resolvedField);
|
|
6153
|
+
}
|
|
6154
|
+
applyCardFields() {
|
|
6155
|
+
this.cardFields.set(this.allAvailableCardFields());
|
|
6156
|
+
}
|
|
5728
6157
|
resetSorts() {
|
|
5729
6158
|
this.applyViewSorts();
|
|
5730
6159
|
}
|
|
@@ -7263,6 +7692,234 @@ function resolveEntityPluginDetailPageOrder(input, slot, options) {
|
|
|
7263
7692
|
* Consumers that only need the token/interface can import from @acorex/platform/domain.
|
|
7264
7693
|
*/
|
|
7265
7694
|
|
|
7695
|
+
/** Component key used when display is 'page'. Register a page component with this key via AXP_PAGE_COMPONENT_PROVIDER. */
|
|
7696
|
+
const ATTACHMENTS_PAGE_COMPONENT_KEY = 'entity-attachments-page';
|
|
7697
|
+
/** Default section order for attachments when display is 'section' (after meta-data-form). */
|
|
7698
|
+
const DEFAULT_ATTACHMENTS_SECTION_ORDER = 200;
|
|
7699
|
+
const DEFAULT_ATTACHMENTS_PAGE_ICON = 'fa-light fa-paperclip';
|
|
7700
|
+
function buildAttachmentsFileUploaderOptions(opts, displayMode) {
|
|
7701
|
+
return {
|
|
7702
|
+
multiple: opts.multiple ?? true,
|
|
7703
|
+
accept: opts.accept,
|
|
7704
|
+
readonly: opts['readonly'] ?? false,
|
|
7705
|
+
fileEditable: opts.fileEditable ?? true,
|
|
7706
|
+
editDialog: opts.editDialog,
|
|
7707
|
+
plugins: opts.plugins ?? [],
|
|
7708
|
+
showBorder: opts.showBorder ?? false,
|
|
7709
|
+
showAddItemButton: opts.showAddItemButton ?? displayMode !== 'page',
|
|
7710
|
+
};
|
|
7711
|
+
}
|
|
7712
|
+
function buildAttachmentsListActionCommandOptions(opts, field) {
|
|
7713
|
+
const base = buildAttachmentsFileUploaderOptions(opts, 'section');
|
|
7714
|
+
return {
|
|
7715
|
+
id: '{{ context.eval("id") }}',
|
|
7716
|
+
key: field,
|
|
7717
|
+
...base,
|
|
7718
|
+
accept: opts.accept ?? '.jpg,.jpeg,.png,.gif,.webp',
|
|
7719
|
+
showAddItemButton: true,
|
|
7720
|
+
};
|
|
7721
|
+
}
|
|
7722
|
+
function applyAttachmentsFileUploaderOptions(ctx, field, opts, displayMode) {
|
|
7723
|
+
const fileUploaderOptions = {
|
|
7724
|
+
...buildAttachmentsFileUploaderOptions(opts, displayMode),
|
|
7725
|
+
entityName: `${ctx.entity.module}.${ctx.entity.name}`,
|
|
7726
|
+
entityId: '{{ context.eval("id") }}',
|
|
7727
|
+
entityField: field,
|
|
7728
|
+
};
|
|
7729
|
+
const prop = (ctx.properties.list() ?? []).find((p) => p.name === field);
|
|
7730
|
+
if (prop?.schema?.interface?.type === 'attachments') {
|
|
7731
|
+
prop.schema.interface.options = {
|
|
7732
|
+
...(prop.schema.interface.options ?? {}),
|
|
7733
|
+
...fileUploaderOptions,
|
|
7734
|
+
};
|
|
7735
|
+
}
|
|
7736
|
+
}
|
|
7737
|
+
/**
|
|
7738
|
+
* Attachments plugin.
|
|
7739
|
+
* - Always ensures an attachments group and property; when display is 'section' (default), adds a section to single/create/update layouts.
|
|
7740
|
+
* - When display is 'page', adds a page to entity.pages with ATTACHMENTS_PAGE_COMPONENT_KEY; register a page component with that key via AXP_PAGE_COMPONENT_PROVIDER.
|
|
7741
|
+
* - List column and list upload action can be turned off with `showListColumn` / `showListAction` (both default true).
|
|
7742
|
+
* - Uses provided accept/multiple/fileEditable; others are fixed.
|
|
7743
|
+
*/
|
|
7744
|
+
const attachmentsPlugin = {
|
|
7745
|
+
name: 'attachments',
|
|
7746
|
+
order: 50,
|
|
7747
|
+
apply: (ctx, options) => {
|
|
7748
|
+
const opts = options ?? {};
|
|
7749
|
+
const field = opts.field && opts.field.trim().length > 0 ? opts.field : 'attachments';
|
|
7750
|
+
const displayTitle = opts.title ?? '@document-management:terms.common.attachments';
|
|
7751
|
+
const displayMode = opts.display ?? 'section';
|
|
7752
|
+
const sectionTitle = opts.section?.title ?? displayTitle;
|
|
7753
|
+
// Ensure group and section ids (unique per field)
|
|
7754
|
+
const sectionId = `attachments-${field}`;
|
|
7755
|
+
const groupIdForProperty = `attachments-${field}`;
|
|
7756
|
+
const existingGroups = ctx.groups.list() ?? [];
|
|
7757
|
+
if (!existingGroups.some((g) => g.id === groupIdForProperty)) {
|
|
7758
|
+
ctx.groups.add({ id: groupIdForProperty, title: displayTitle });
|
|
7759
|
+
}
|
|
7760
|
+
// Ensure property exists (using a generic file-uploader interface)
|
|
7761
|
+
const props = ctx.properties.list();
|
|
7762
|
+
if (!props.some((p) => p.name === field)) {
|
|
7763
|
+
ctx.properties.add({
|
|
7764
|
+
name: field,
|
|
7765
|
+
title: displayTitle,
|
|
7766
|
+
groupId: groupIdForProperty,
|
|
7767
|
+
schema: {
|
|
7768
|
+
dataType: 'string',
|
|
7769
|
+
interface: {
|
|
7770
|
+
type: 'attachments',
|
|
7771
|
+
options: {
|
|
7772
|
+
...buildAttachmentsFileUploaderOptions({ ...opts, field }, displayMode),
|
|
7773
|
+
entityName: `${ctx.entity.module}.${ctx.entity.name}`,
|
|
7774
|
+
entityId: '{{ context.eval("id") }}',
|
|
7775
|
+
entityField: field,
|
|
7776
|
+
},
|
|
7777
|
+
},
|
|
7778
|
+
},
|
|
7779
|
+
});
|
|
7780
|
+
}
|
|
7781
|
+
applyAttachmentsFileUploaderOptions(ctx, field, opts, displayMode);
|
|
7782
|
+
// Ensure column exists (optional)
|
|
7783
|
+
const showListColumn = opts.showListColumn ?? true;
|
|
7784
|
+
if (showListColumn) {
|
|
7785
|
+
const cols = ctx.columns.list() ?? [];
|
|
7786
|
+
if (!cols?.some((c) => c.name === field)) {
|
|
7787
|
+
ctx.columns.add({ name: field });
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
if (displayMode === 'section') {
|
|
7791
|
+
const propertyLayoutOrder = 8;
|
|
7792
|
+
const layoutSectionOrder = opts.section?.order ?? DEFAULT_ATTACHMENTS_SECTION_ORDER;
|
|
7793
|
+
const colSpan = 12;
|
|
7794
|
+
ctx.interfaces.master.single.update((single) => {
|
|
7795
|
+
const next = cloneLayoutArrays((single ?? { title: ctx.entity.title, sections: [], properties: [] }));
|
|
7796
|
+
ensureLayoutSection(next, { id: sectionId, title: sectionTitle, order: layoutSectionOrder });
|
|
7797
|
+
ensureLayoutPropertyView(next, {
|
|
7798
|
+
name: field,
|
|
7799
|
+
layout: { label: { visible: false }, positions: { lg: { colSpan, order: propertyLayoutOrder } } },
|
|
7800
|
+
});
|
|
7801
|
+
return next;
|
|
7802
|
+
});
|
|
7803
|
+
ctx.interfaces.master.create.update((create) => {
|
|
7804
|
+
const next = cloneLayoutArrays((create ?? { sections: [], properties: [] }));
|
|
7805
|
+
ensureLayoutSection(next, { id: sectionId, title: sectionTitle, order: layoutSectionOrder });
|
|
7806
|
+
ensureLayoutPropertyView(next, {
|
|
7807
|
+
name: field,
|
|
7808
|
+
layout: { label: { visible: false }, positions: { lg: { colSpan, order: 6 } } },
|
|
7809
|
+
});
|
|
7810
|
+
return next;
|
|
7811
|
+
});
|
|
7812
|
+
ctx.interfaces.master.modify.update((modify) => {
|
|
7813
|
+
const next = cloneLayoutArrays((modify ?? { sections: [], properties: [] }));
|
|
7814
|
+
ensureLayoutSection(next, { id: sectionId, title: sectionTitle, order: layoutSectionOrder });
|
|
7815
|
+
ensureLayoutPropertyView(next, {
|
|
7816
|
+
name: field,
|
|
7817
|
+
layout: { label: { visible: false }, positions: { lg: { colSpan, order: 6 } } },
|
|
7818
|
+
});
|
|
7819
|
+
return next;
|
|
7820
|
+
});
|
|
7821
|
+
}
|
|
7822
|
+
else {
|
|
7823
|
+
ctx.entity.pages ??= [];
|
|
7824
|
+
const pageExists = ctx.entity.pages.some((p) => p.componentKey === ATTACHMENTS_PAGE_COMPONENT_KEY && p.field === field);
|
|
7825
|
+
const pageOrder = resolveEntityPluginDetailPageOrder({ relatedEntities: ctx.entity.relatedEntities, pages: ctx.entity.pages }, 'attachments', {
|
|
7826
|
+
componentKey: ATTACHMENTS_PAGE_COMPONENT_KEY,
|
|
7827
|
+
field,
|
|
7828
|
+
skipPage: pageExists
|
|
7829
|
+
? { componentKey: ATTACHMENTS_PAGE_COMPONENT_KEY, field }
|
|
7830
|
+
: undefined,
|
|
7831
|
+
});
|
|
7832
|
+
const page = {
|
|
7833
|
+
componentKey: ATTACHMENTS_PAGE_COMPONENT_KEY,
|
|
7834
|
+
title: displayTitle,
|
|
7835
|
+
icon: DEFAULT_ATTACHMENTS_PAGE_ICON,
|
|
7836
|
+
layout: { order: pageOrder },
|
|
7837
|
+
field,
|
|
7838
|
+
};
|
|
7839
|
+
if (!pageExists) {
|
|
7840
|
+
ctx.entity.pages.push(page);
|
|
7841
|
+
}
|
|
7842
|
+
else {
|
|
7843
|
+
const pages = ctx.entity.pages;
|
|
7844
|
+
const index = pages.findIndex((p) => p.componentKey === ATTACHMENTS_PAGE_COMPONENT_KEY && p.field === field);
|
|
7845
|
+
if (index >= 0) {
|
|
7846
|
+
pages[index] = { ...pages[index], ...page };
|
|
7847
|
+
}
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
// Ensure list action to open file uploader popup exists (optional)
|
|
7851
|
+
const showListAction = opts.showListAction ?? true;
|
|
7852
|
+
if (showListAction) {
|
|
7853
|
+
ensureListActions(ctx);
|
|
7854
|
+
ctx.interfaces.update((i) => {
|
|
7855
|
+
const actions = i.master.list.actions;
|
|
7856
|
+
const cmdName = 'show-file-uploader-popup';
|
|
7857
|
+
const actionName = `attachments-${field}`;
|
|
7858
|
+
if (!actionExists(actions, cmdName, actionName)) {
|
|
7859
|
+
actions.push({
|
|
7860
|
+
name: actionName,
|
|
7861
|
+
title: '@document-management:actions.upload-files',
|
|
7862
|
+
command: {
|
|
7863
|
+
name: cmdName,
|
|
7864
|
+
options: { ...buildAttachmentsListActionCommandOptions(opts, field),
|
|
7865
|
+
entity: {
|
|
7866
|
+
name: `${ctx.entity.module}.${ctx.entity.name}`,
|
|
7867
|
+
id: '{{ context.eval("id") }}',
|
|
7868
|
+
field: field,
|
|
7869
|
+
},
|
|
7870
|
+
},
|
|
7871
|
+
},
|
|
7872
|
+
priority: 'secondary',
|
|
7873
|
+
type: 'upload',
|
|
7874
|
+
scope: AXPEntityCommandScope.Individual,
|
|
7875
|
+
});
|
|
7876
|
+
}
|
|
7877
|
+
return i;
|
|
7878
|
+
});
|
|
7879
|
+
}
|
|
7880
|
+
// Stamp normalized config for middleware
|
|
7881
|
+
ctx.entity.extensions ??= {};
|
|
7882
|
+
ctx.entity.extensions.attachments ??= {};
|
|
7883
|
+
ctx.entity.extensions.attachments[field] = { title: displayTitle, display: displayMode };
|
|
7884
|
+
},
|
|
7885
|
+
};
|
|
7886
|
+
|
|
7887
|
+
//#region ---- Attachments Page Component Provider ----
|
|
7888
|
+
class AXMAttachmentsPageComponentProvider {
|
|
7889
|
+
async components() {
|
|
7890
|
+
return [
|
|
7891
|
+
{
|
|
7892
|
+
key: ATTACHMENTS_PAGE_COMPONENT_KEY,
|
|
7893
|
+
loader: () => import('./acorex-platform-layout-entity-attachments-page.component-BaTS183I.mjs').then((m) => m.AXMAttachmentsPageComponent),
|
|
7894
|
+
},
|
|
7895
|
+
];
|
|
7896
|
+
}
|
|
7897
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMAttachmentsPageComponentProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7898
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMAttachmentsPageComponentProvider }); }
|
|
7899
|
+
}
|
|
7900
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMAttachmentsPageComponentProvider, decorators: [{
|
|
7901
|
+
type: Injectable
|
|
7902
|
+
}] });
|
|
7903
|
+
|
|
7904
|
+
class AXPAttachmentsPluginModule {
|
|
7905
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAttachmentsPluginModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
7906
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPAttachmentsPluginModule }); }
|
|
7907
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAttachmentsPluginModule, providers: [{ provide: AXP_ENTITY_ACTION_PLUGIN, multi: true, useValue: attachmentsPlugin },
|
|
7908
|
+
{ provide: AXP_PAGE_COMPONENT_PROVIDER, multi: true, useClass: AXMAttachmentsPageComponentProvider },
|
|
7909
|
+
] }); }
|
|
7910
|
+
}
|
|
7911
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAttachmentsPluginModule, decorators: [{
|
|
7912
|
+
type: NgModule,
|
|
7913
|
+
args: [{
|
|
7914
|
+
declarations: [],
|
|
7915
|
+
imports: [],
|
|
7916
|
+
exports: [],
|
|
7917
|
+
providers: [{ provide: AXP_ENTITY_ACTION_PLUGIN, multi: true, useValue: attachmentsPlugin },
|
|
7918
|
+
{ provide: AXP_PAGE_COMPONENT_PROVIDER, multi: true, useClass: AXMAttachmentsPageComponentProvider },
|
|
7919
|
+
],
|
|
7920
|
+
}]
|
|
7921
|
+
}] });
|
|
7922
|
+
|
|
7266
7923
|
//#region ---- Constants ----
|
|
7267
7924
|
/**
|
|
7268
7925
|
* i18n key for the synthetic root node ("all categories"). Resolve in templates with the translate pipe.
|
|
@@ -11339,14 +11996,22 @@ function mapPropertyToWidgetColumn(property) {
|
|
|
11339
11996
|
*/
|
|
11340
11997
|
function mapEntityColumnToWidgetColumn(entity, column) {
|
|
11341
11998
|
const property = entity.properties.find((p) => p.name === column.name);
|
|
11999
|
+
const interfaceType = property?.schema?.interface?.type;
|
|
11342
12000
|
const widgetFromProperty = property?.schema?.interface
|
|
11343
12001
|
? {
|
|
11344
|
-
type:
|
|
12002
|
+
type: interfaceType || 'text-editor',
|
|
11345
12003
|
path: column.options?.dataPath || column.name,
|
|
11346
12004
|
options: {
|
|
11347
12005
|
readonly: true,
|
|
11348
12006
|
columnName: column.name,
|
|
11349
12007
|
...property.schema.interface.options,
|
|
12008
|
+
...(interfaceType === 'attachments'
|
|
12009
|
+
? {
|
|
12010
|
+
entityName: property.schema.interface.options?.['entityName'] ??
|
|
12011
|
+
`${entity.module}.${entity.name}`,
|
|
12012
|
+
entityField: property.schema.interface.options?.['entityField'] ?? column.name,
|
|
12013
|
+
}
|
|
12014
|
+
: {}),
|
|
11350
12015
|
...(property.schema.interface.children?.length && { children: property.schema.interface.children }),
|
|
11351
12016
|
},
|
|
11352
12017
|
}
|
|
@@ -12859,54 +13524,352 @@ var lookupWidgetView_component = /*#__PURE__*/Object.freeze({
|
|
|
12859
13524
|
});
|
|
12860
13525
|
|
|
12861
13526
|
//#endregion
|
|
12862
|
-
class
|
|
13527
|
+
class AXPEntityDataSelectorRowActionsService {
|
|
12863
13528
|
constructor() {
|
|
12864
13529
|
//#region ---- Services & Dependencies ----
|
|
12865
|
-
this.
|
|
12866
|
-
this.
|
|
12867
|
-
this.widgetResolver = inject(AXPWidgetRegistryService);
|
|
12868
|
-
this.entityResolver = inject(AXPEntityDefinitionRegistryService);
|
|
13530
|
+
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
13531
|
+
this.workflow = inject(AXPWorkflowService);
|
|
12869
13532
|
this.commandService = inject(AXPCommandService);
|
|
12870
13533
|
}
|
|
12871
13534
|
//#endregion
|
|
12872
13535
|
//#region ---- Public Methods ----
|
|
12873
13536
|
/**
|
|
12874
|
-
*
|
|
13537
|
+
* Build a row-actions handler for {@link AXPDataSelectorComponent} from entity list actions.
|
|
12875
13538
|
*/
|
|
12876
|
-
|
|
12877
|
-
const
|
|
12878
|
-
|
|
12879
|
-
if (
|
|
12880
|
-
|
|
12881
|
-
config.categoryFilter = {
|
|
12882
|
-
enabled: true,
|
|
12883
|
-
...options.categoryFilter,
|
|
12884
|
-
};
|
|
13539
|
+
createHandler(entity, config) {
|
|
13540
|
+
const primaryFilters = config.primary ?? [];
|
|
13541
|
+
const secondaryFilters = config.secondary ?? [];
|
|
13542
|
+
if (primaryFilters.length === 0 && secondaryFilters.length === 0) {
|
|
13543
|
+
return undefined;
|
|
12885
13544
|
}
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
if (categoryExt) {
|
|
12892
|
-
const categoryTreeDataSource = await this.createCategoryTreeDataSource(categoryExt.entity);
|
|
12893
|
-
if (categoryTreeDataSource) {
|
|
12894
|
-
const propertyName = categoryExt.propertyName || 'categoryIds';
|
|
12895
|
-
const filterOperator = 'contains';
|
|
12896
|
-
config.categoryFilter = {
|
|
12897
|
-
enabled: true,
|
|
12898
|
-
title: '@general:terms.classification.categories',
|
|
12899
|
-
dataSource: categoryTreeDataSource,
|
|
12900
|
-
filterField: propertyName,
|
|
12901
|
-
filterOperator,
|
|
12902
|
-
};
|
|
12903
|
-
}
|
|
12904
|
-
}
|
|
12905
|
-
}
|
|
13545
|
+
const allActions = this.buildIndividualActions(entity);
|
|
13546
|
+
const primaryActions = this.filterActions(allActions, primaryFilters);
|
|
13547
|
+
const secondaryActions = this.filterActions(allActions, secondaryFilters);
|
|
13548
|
+
if (primaryActions.length === 0 && secondaryActions.length === 0) {
|
|
13549
|
+
return undefined;
|
|
12906
13550
|
}
|
|
12907
|
-
const
|
|
12908
|
-
return
|
|
12909
|
-
|
|
13551
|
+
const flattenedActions = this.flattenActions([...primaryActions, ...secondaryActions]);
|
|
13552
|
+
return {
|
|
13553
|
+
hasPrimary: () => primaryActions.length > 0,
|
|
13554
|
+
hasDropdown: () => secondaryActions.length > 0,
|
|
13555
|
+
getPrimaryItems: () => this.toRowCommandItems(primaryActions),
|
|
13556
|
+
getDropdownItems: async (rowData) => {
|
|
13557
|
+
const evaluated = await this.evaluateActionsForRow(secondaryActions, rowData);
|
|
13558
|
+
return this.toRowCommandItems(evaluated);
|
|
13559
|
+
},
|
|
13560
|
+
execute: async (commandName, rowData) => {
|
|
13561
|
+
await this.executeCommand(entity, flattenedActions, commandName, rowData);
|
|
13562
|
+
},
|
|
13563
|
+
};
|
|
13564
|
+
}
|
|
13565
|
+
//#endregion
|
|
13566
|
+
//#region ---- Private Methods ----
|
|
13567
|
+
buildIndividualActions(entity) {
|
|
13568
|
+
const listActions = entity.interfaces?.master?.list?.actions ?? [];
|
|
13569
|
+
const individualActions = listActions.filter((action) => action.scope === AXPEntityCommandScope.Individual);
|
|
13570
|
+
return orderBy(individualActions.map((action) => new AXPEntityCommandTriggerViewModel(entity, action)), 'order', 'asc');
|
|
13571
|
+
}
|
|
13572
|
+
filterActions(actions, filters) {
|
|
13573
|
+
if (!filters.length) {
|
|
13574
|
+
return [];
|
|
13575
|
+
}
|
|
13576
|
+
return actions.filter((action) => this.matchesActionFilter(action, filters));
|
|
13577
|
+
}
|
|
13578
|
+
matchesActionFilter(action, filters) {
|
|
13579
|
+
const commandName = this.getCommandNameFromTrigger(action.name);
|
|
13580
|
+
return filters.some((filter) => {
|
|
13581
|
+
const normalizedFilter = filter.trim();
|
|
13582
|
+
if (!normalizedFilter) {
|
|
13583
|
+
return false;
|
|
13584
|
+
}
|
|
13585
|
+
return (commandName === normalizedFilter ||
|
|
13586
|
+
action.name === normalizedFilter ||
|
|
13587
|
+
commandName.endsWith(normalizedFilter) ||
|
|
13588
|
+
action.name.endsWith(normalizedFilter) ||
|
|
13589
|
+
action.name.startsWith(`${normalizedFilter}&`));
|
|
13590
|
+
});
|
|
13591
|
+
}
|
|
13592
|
+
getCommandNameFromTrigger(triggerName) {
|
|
13593
|
+
const ampIndex = triggerName.indexOf('&');
|
|
13594
|
+
return ampIndex >= 0 ? triggerName.slice(0, ampIndex) : triggerName;
|
|
13595
|
+
}
|
|
13596
|
+
flattenActions(actions) {
|
|
13597
|
+
const result = [];
|
|
13598
|
+
const visit = (items) => {
|
|
13599
|
+
for (const item of items) {
|
|
13600
|
+
result.push(item);
|
|
13601
|
+
if (item.items?.length) {
|
|
13602
|
+
visit(item.items);
|
|
13603
|
+
}
|
|
13604
|
+
}
|
|
13605
|
+
};
|
|
13606
|
+
visit(actions);
|
|
13607
|
+
return result;
|
|
13608
|
+
}
|
|
13609
|
+
toRowCommandItems(actions) {
|
|
13610
|
+
return actions.map((action) => ({
|
|
13611
|
+
name: action.name,
|
|
13612
|
+
text: translateSync(action.title),
|
|
13613
|
+
icon: action.icon ?? '',
|
|
13614
|
+
color: action.color,
|
|
13615
|
+
disabled: action.disabled,
|
|
13616
|
+
default: action.default,
|
|
13617
|
+
}));
|
|
13618
|
+
}
|
|
13619
|
+
async evaluateActionsForRow(actions, rowData) {
|
|
13620
|
+
const scope = this.createExpressionScope(rowData);
|
|
13621
|
+
const evaluated = await Promise.all(actions.map(async (action) => {
|
|
13622
|
+
const isHidden = await this.expressionEvaluator.evaluate(action.hidden, scope);
|
|
13623
|
+
if (isHidden) {
|
|
13624
|
+
return null;
|
|
13625
|
+
}
|
|
13626
|
+
const disabled = await this.expressionEvaluator.evaluate(action.disabled, scope);
|
|
13627
|
+
const name = await this.resolveTriggerName(action.name, rowData);
|
|
13628
|
+
const title = await this.resolveTitleExpression(action.title, rowData);
|
|
13629
|
+
return { ...action, disabled, name, title };
|
|
13630
|
+
}));
|
|
13631
|
+
return evaluated.filter(Boolean);
|
|
13632
|
+
}
|
|
13633
|
+
createExpressionScope(data) {
|
|
13634
|
+
return {
|
|
13635
|
+
context: {
|
|
13636
|
+
eval: (path) => get(data, path),
|
|
13637
|
+
},
|
|
13638
|
+
};
|
|
13639
|
+
}
|
|
13640
|
+
async resolveTriggerName(triggerName, data) {
|
|
13641
|
+
if (!triggerName) {
|
|
13642
|
+
return triggerName;
|
|
13643
|
+
}
|
|
13644
|
+
const ampIndex = triggerName.indexOf('&');
|
|
13645
|
+
if (ampIndex < 0) {
|
|
13646
|
+
return this.resolveCommandNameExpression(triggerName, data);
|
|
13647
|
+
}
|
|
13648
|
+
const commandPart = triggerName.slice(0, ampIndex);
|
|
13649
|
+
const suffix = triggerName.slice(ampIndex);
|
|
13650
|
+
const resolvedCommand = await this.resolveCommandNameExpression(commandPart, data);
|
|
13651
|
+
return `${resolvedCommand}${suffix}`;
|
|
13652
|
+
}
|
|
13653
|
+
async resolveCommandNameExpression(commandName, data) {
|
|
13654
|
+
if (!commandName || !commandName.includes('{{')) {
|
|
13655
|
+
return commandName;
|
|
13656
|
+
}
|
|
13657
|
+
const scope = this.createExpressionScope(data);
|
|
13658
|
+
const result = await this.expressionEvaluator.evaluate({ value: commandName }, scope);
|
|
13659
|
+
const resolved = result?.value;
|
|
13660
|
+
return typeof resolved === 'string' && resolved.trim() !== '' ? resolved : commandName;
|
|
13661
|
+
}
|
|
13662
|
+
async resolveTitleExpression(title, data) {
|
|
13663
|
+
if (!title || !title.includes('{{')) {
|
|
13664
|
+
return title;
|
|
13665
|
+
}
|
|
13666
|
+
const scope = this.createExpressionScope(data);
|
|
13667
|
+
const result = await this.expressionEvaluator.evaluate({ value: title }, scope);
|
|
13668
|
+
const resolved = result?.value;
|
|
13669
|
+
return typeof resolved === 'string' && resolved.trim() !== '' ? resolved : title;
|
|
13670
|
+
}
|
|
13671
|
+
async executeCommand(entity, actions, commandName, rowData) {
|
|
13672
|
+
const action = actions.find((candidate) => candidate.name === commandName);
|
|
13673
|
+
if (!action) {
|
|
13674
|
+
return;
|
|
13675
|
+
}
|
|
13676
|
+
const resolvedTriggerName = await this.resolveTriggerName(commandName, rowData);
|
|
13677
|
+
const command = resolvedTriggerName.split('&')[0];
|
|
13678
|
+
const options = await this.evaluateActionOptions(action.options, rowData);
|
|
13679
|
+
if (this.workflow.exists(command)) {
|
|
13680
|
+
await this.workflow.execute(command, {
|
|
13681
|
+
entity: getEntityInfo(entity).source,
|
|
13682
|
+
entityInfo: {
|
|
13683
|
+
name: entity.name,
|
|
13684
|
+
module: entity.module,
|
|
13685
|
+
title: entity.title,
|
|
13686
|
+
parentKey: entity.parentKey,
|
|
13687
|
+
source: `${entity.module}.${entity.name}`,
|
|
13688
|
+
},
|
|
13689
|
+
data: rowData,
|
|
13690
|
+
options,
|
|
13691
|
+
metadata: action.metadata,
|
|
13692
|
+
});
|
|
13693
|
+
return;
|
|
13694
|
+
}
|
|
13695
|
+
await this.commandService.execute(command, {
|
|
13696
|
+
__context__: {
|
|
13697
|
+
data: rowData,
|
|
13698
|
+
entity: getEntityInfo(entity).source,
|
|
13699
|
+
entityInfo: {
|
|
13700
|
+
name: entity.name,
|
|
13701
|
+
module: entity.module,
|
|
13702
|
+
title: entity.title,
|
|
13703
|
+
parentKey: entity.parentKey,
|
|
13704
|
+
},
|
|
13705
|
+
options,
|
|
13706
|
+
},
|
|
13707
|
+
__metadata__: action.metadata,
|
|
13708
|
+
});
|
|
13709
|
+
}
|
|
13710
|
+
async evaluateActionOptions(options, rowData) {
|
|
13711
|
+
if (!options || typeof options !== 'object') {
|
|
13712
|
+
return {};
|
|
13713
|
+
}
|
|
13714
|
+
const scope = this.createExpressionScope(rowData);
|
|
13715
|
+
const evaluated = await this.expressionEvaluator.evaluate(options, scope);
|
|
13716
|
+
return evaluated ?? {};
|
|
13717
|
+
}
|
|
13718
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDataSelectorRowActionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
13719
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDataSelectorRowActionsService, providedIn: 'root' }); }
|
|
13720
|
+
}
|
|
13721
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDataSelectorRowActionsService, decorators: [{
|
|
13722
|
+
type: Injectable,
|
|
13723
|
+
args: [{
|
|
13724
|
+
providedIn: 'root',
|
|
13725
|
+
}]
|
|
13726
|
+
}] });
|
|
13727
|
+
|
|
13728
|
+
const LEGACY_ENTITY_DATA_SELECTOR_OPTION_KEYS = [
|
|
13729
|
+
'allowMultiple',
|
|
13730
|
+
'allowUnselect',
|
|
13731
|
+
'filters',
|
|
13732
|
+
'parentFilters',
|
|
13733
|
+
'columns',
|
|
13734
|
+
'sortedFields',
|
|
13735
|
+
'allowCreate',
|
|
13736
|
+
'searchFields',
|
|
13737
|
+
'initialSearchTerm',
|
|
13738
|
+
'selectedItemIds',
|
|
13739
|
+
'categoryFilter',
|
|
13740
|
+
'showCategoryFilter',
|
|
13741
|
+
];
|
|
13742
|
+
//#endregion
|
|
13743
|
+
//#region ---- Normalization ----
|
|
13744
|
+
/**
|
|
13745
|
+
* Returns true when flat (legacy) option fields are present on the input object.
|
|
13746
|
+
*/
|
|
13747
|
+
function isLegacyEntityDataSelectorOptions(options) {
|
|
13748
|
+
const legacy = options;
|
|
13749
|
+
return LEGACY_ENTITY_DATA_SELECTOR_OPTION_KEYS.some((key) => legacy[key] !== undefined);
|
|
13750
|
+
}
|
|
13751
|
+
/**
|
|
13752
|
+
* Maps deprecated flat options to {@link AXPEntityDataSelectorOpenOptions}.
|
|
13753
|
+
*/
|
|
13754
|
+
function mapLegacyEntityDataSelectorOptions(legacy) {
|
|
13755
|
+
const normalized = {
|
|
13756
|
+
entity: legacy.entity,
|
|
13757
|
+
title: legacy.title,
|
|
13758
|
+
};
|
|
13759
|
+
if (legacy.allowMultiple !== undefined ||
|
|
13760
|
+
legacy.selectedItemIds !== undefined ||
|
|
13761
|
+
legacy.allowUnselect !== undefined) {
|
|
13762
|
+
normalized.selection = {
|
|
13763
|
+
...(legacy.allowMultiple !== undefined && { multiple: legacy.allowMultiple }),
|
|
13764
|
+
...(legacy.selectedItemIds !== undefined && { selectedItemIds: legacy.selectedItemIds }),
|
|
13765
|
+
...(legacy.allowUnselect !== undefined && { allowUnselect: legacy.allowUnselect }),
|
|
13766
|
+
};
|
|
13767
|
+
}
|
|
13768
|
+
if (legacy.columns !== undefined) {
|
|
13769
|
+
normalized.grid = { columns: legacy.columns };
|
|
13770
|
+
}
|
|
13771
|
+
if (legacy.filters !== undefined || legacy.parentFilters !== undefined) {
|
|
13772
|
+
normalized.filter = {
|
|
13773
|
+
...(legacy.filters !== undefined && { value: legacy.filters }),
|
|
13774
|
+
...(legacy.parentFilters !== undefined && { parent: legacy.parentFilters }),
|
|
13775
|
+
};
|
|
13776
|
+
}
|
|
13777
|
+
if (legacy.searchFields !== undefined || legacy.initialSearchTerm !== undefined) {
|
|
13778
|
+
normalized.search = {
|
|
13779
|
+
...(legacy.searchFields !== undefined && { fields: legacy.searchFields }),
|
|
13780
|
+
...(legacy.initialSearchTerm !== undefined && { initial: legacy.initialSearchTerm }),
|
|
13781
|
+
};
|
|
13782
|
+
}
|
|
13783
|
+
if (legacy.sortedFields !== undefined) {
|
|
13784
|
+
normalized.sort = { fields: legacy.sortedFields };
|
|
13785
|
+
}
|
|
13786
|
+
if (legacy.categoryFilter) {
|
|
13787
|
+
normalized.category = {
|
|
13788
|
+
enabled: true,
|
|
13789
|
+
title: legacy.categoryFilter.title,
|
|
13790
|
+
width: legacy.categoryFilter.width,
|
|
13791
|
+
dataSource: legacy.categoryFilter.dataSource,
|
|
13792
|
+
filter: {
|
|
13793
|
+
field: legacy.categoryFilter.filterField,
|
|
13794
|
+
operator: legacy.categoryFilter.filterOperator,
|
|
13795
|
+
},
|
|
13796
|
+
};
|
|
13797
|
+
}
|
|
13798
|
+
else if (legacy.showCategoryFilter !== undefined) {
|
|
13799
|
+
normalized.category = { enabled: legacy.showCategoryFilter };
|
|
13800
|
+
}
|
|
13801
|
+
if (legacy.allowCreate !== undefined) {
|
|
13802
|
+
normalized.create = { mode: legacy.allowCreate };
|
|
13803
|
+
}
|
|
13804
|
+
if (legacy.rowActions !== undefined) {
|
|
13805
|
+
normalized.rowActions = legacy.rowActions;
|
|
13806
|
+
}
|
|
13807
|
+
return normalized;
|
|
13808
|
+
}
|
|
13809
|
+
/**
|
|
13810
|
+
* Normalizes legacy flat or nested options to {@link AXPEntityDataSelectorOpenOptions}.
|
|
13811
|
+
* When both shapes are mixed, nested groups take precedence over mapped legacy values.
|
|
13812
|
+
*/
|
|
13813
|
+
function normalizeEntityDataSelectorOptions(options) {
|
|
13814
|
+
const normalized = isLegacyEntityDataSelectorOptions(options)
|
|
13815
|
+
? mapLegacyEntityDataSelectorOptions(options)
|
|
13816
|
+
: { ...options };
|
|
13817
|
+
const nested = options;
|
|
13818
|
+
if (nested.selection) {
|
|
13819
|
+
normalized.selection = { ...normalized.selection, ...nested.selection };
|
|
13820
|
+
}
|
|
13821
|
+
if (nested.grid) {
|
|
13822
|
+
normalized.grid = { ...normalized.grid, ...nested.grid };
|
|
13823
|
+
}
|
|
13824
|
+
if (nested.filter) {
|
|
13825
|
+
normalized.filter = { ...normalized.filter, ...nested.filter };
|
|
13826
|
+
}
|
|
13827
|
+
if (nested.search) {
|
|
13828
|
+
normalized.search = { ...normalized.search, ...nested.search };
|
|
13829
|
+
}
|
|
13830
|
+
if (nested.sort) {
|
|
13831
|
+
normalized.sort = { ...normalized.sort, ...nested.sort };
|
|
13832
|
+
}
|
|
13833
|
+
if (nested.category) {
|
|
13834
|
+
normalized.category = { ...normalized.category, ...nested.category };
|
|
13835
|
+
if (nested.category.filter) {
|
|
13836
|
+
normalized.category.filter = { ...normalized.category?.filter, ...nested.category.filter };
|
|
13837
|
+
}
|
|
13838
|
+
}
|
|
13839
|
+
if (nested.create) {
|
|
13840
|
+
normalized.create = { ...normalized.create, ...nested.create };
|
|
13841
|
+
}
|
|
13842
|
+
if (nested.rowActions !== undefined) {
|
|
13843
|
+
normalized.rowActions = nested.rowActions;
|
|
13844
|
+
}
|
|
13845
|
+
return normalized;
|
|
13846
|
+
}
|
|
13847
|
+
//#endregion
|
|
13848
|
+
class AXPEntityDataSelectorService {
|
|
13849
|
+
constructor() {
|
|
13850
|
+
//#region ---- Services & Dependencies ----
|
|
13851
|
+
this.dataSelectorService = inject(AXPDataSelectorService);
|
|
13852
|
+
this.filterOperatorMiddleware = inject(AXPFilterOperatorMiddlewareService);
|
|
13853
|
+
this.widgetResolver = inject(AXPWidgetRegistryService);
|
|
13854
|
+
this.entityResolver = inject(AXPEntityDefinitionRegistryService);
|
|
13855
|
+
this.commandService = inject(AXPCommandService);
|
|
13856
|
+
this.categoryTreeService = inject(AXPCategoryTreeService);
|
|
13857
|
+
this.translationService = inject(AXTranslationService);
|
|
13858
|
+
this.rowActionsService = inject(AXPEntityDataSelectorRowActionsService);
|
|
13859
|
+
}
|
|
13860
|
+
//#endregion
|
|
13861
|
+
//#region ---- Public Methods ----
|
|
13862
|
+
/**
|
|
13863
|
+
* Open entity data selector popup.
|
|
13864
|
+
* Accepts nested {@link AXPEntityDataSelectorOpenOptions} or deprecated flat {@link AXPEntityDataSelectorOptions}.
|
|
13865
|
+
*/
|
|
13866
|
+
async open(options) {
|
|
13867
|
+
const normalized = normalizeEntityDataSelectorOptions(options);
|
|
13868
|
+
const config = this.createDataSelectorConfig(normalized);
|
|
13869
|
+
await this.applyCategoryFilter(normalized, config);
|
|
13870
|
+
const result = (await this.dataSelectorService.open(config));
|
|
13871
|
+
return result;
|
|
13872
|
+
}
|
|
12910
13873
|
/**
|
|
12911
13874
|
* Execute Entity:Create and return created item.
|
|
12912
13875
|
* Used by lookup widget and data selector for inline create.
|
|
@@ -12923,6 +13886,51 @@ class AXPEntityDataSelectorService {
|
|
|
12923
13886
|
}
|
|
12924
13887
|
//#endregion
|
|
12925
13888
|
//#region ---- Private Methods ----
|
|
13889
|
+
/**
|
|
13890
|
+
* Apply category sidebar config from normalized options or entity plugin auto-detection.
|
|
13891
|
+
*/
|
|
13892
|
+
async applyCategoryFilter(options, config) {
|
|
13893
|
+
const category = options.category;
|
|
13894
|
+
if (category?.dataSource) {
|
|
13895
|
+
config.category = {
|
|
13896
|
+
enabled: category.enabled ?? true,
|
|
13897
|
+
title: category.title ?? '@general:terms.classification.categories',
|
|
13898
|
+
dataSource: category.dataSource,
|
|
13899
|
+
filter: {
|
|
13900
|
+
field: category.filter?.field ?? 'categoryIds',
|
|
13901
|
+
operator: category.filter?.operator,
|
|
13902
|
+
},
|
|
13903
|
+
width: category.width,
|
|
13904
|
+
};
|
|
13905
|
+
return;
|
|
13906
|
+
}
|
|
13907
|
+
if (category?.enabled === false) {
|
|
13908
|
+
return;
|
|
13909
|
+
}
|
|
13910
|
+
const categoryExt = options.entity?.extensions?.category;
|
|
13911
|
+
if (!categoryExt) {
|
|
13912
|
+
return;
|
|
13913
|
+
}
|
|
13914
|
+
const categoryTreeDataSource = await this.createCategoryTreeDataSource(categoryExt.entity, {
|
|
13915
|
+
textField: categoryExt.textField,
|
|
13916
|
+
valueField: categoryExt.valueField,
|
|
13917
|
+
parentKey: categoryExt.parentKey,
|
|
13918
|
+
});
|
|
13919
|
+
if (!categoryTreeDataSource) {
|
|
13920
|
+
return;
|
|
13921
|
+
}
|
|
13922
|
+
const propertyName = categoryExt.propertyName || 'categoryIds';
|
|
13923
|
+
config.category = {
|
|
13924
|
+
enabled: true,
|
|
13925
|
+
title: category?.title ?? '@general:terms.classification.categories',
|
|
13926
|
+
dataSource: categoryTreeDataSource,
|
|
13927
|
+
filter: {
|
|
13928
|
+
field: category?.filter?.field ?? propertyName,
|
|
13929
|
+
operator: category?.filter?.operator ?? 'contains',
|
|
13930
|
+
},
|
|
13931
|
+
width: category?.width,
|
|
13932
|
+
};
|
|
13933
|
+
}
|
|
12926
13934
|
/**
|
|
12927
13935
|
* Create data selector configuration from entity options
|
|
12928
13936
|
*/
|
|
@@ -12930,27 +13938,43 @@ class AXPEntityDataSelectorService {
|
|
|
12930
13938
|
const dataSource = this.createDataSource(options);
|
|
12931
13939
|
const columns = this.createColumns(options);
|
|
12932
13940
|
const searchFields = this.getSearchFields(options);
|
|
12933
|
-
const allowCreate = options.
|
|
13941
|
+
const allowCreate = options.create?.mode ?? this.getAllowCreate(options.entity);
|
|
13942
|
+
const rowActions = options.rowActions
|
|
13943
|
+
? this.rowActionsService.createHandler(options.entity, options.rowActions)
|
|
13944
|
+
: undefined;
|
|
12934
13945
|
return {
|
|
12935
13946
|
title: options.title,
|
|
12936
13947
|
dataSource,
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
13948
|
+
grid: {
|
|
13949
|
+
columns,
|
|
13950
|
+
parentField: options.entity.parentKey,
|
|
13951
|
+
},
|
|
13952
|
+
selection: {
|
|
13953
|
+
mode: options.selection?.multiple ? 'multiple' : 'single',
|
|
13954
|
+
selectedItemIds: options.selection?.selectedItemIds,
|
|
13955
|
+
allowUnselect: options.selection?.allowUnselect ?? true,
|
|
13956
|
+
},
|
|
13957
|
+
search: searchFields.length > 0 || options.search?.initial
|
|
13958
|
+
? {
|
|
13959
|
+
fields: searchFields,
|
|
13960
|
+
initial: options.search?.initial,
|
|
13961
|
+
}
|
|
13962
|
+
: undefined,
|
|
13963
|
+
filter: options.filter?.value ? { value: options.filter.value } : undefined,
|
|
13964
|
+
create: {
|
|
13965
|
+
mode: allowCreate,
|
|
13966
|
+
onCreate: allowCreate !== 'none' ? (mode) => this.executeCreate(options.entity, mode) : undefined,
|
|
13967
|
+
},
|
|
13968
|
+
rowActions,
|
|
12947
13969
|
};
|
|
12948
13970
|
}
|
|
12949
13971
|
/**
|
|
12950
13972
|
* Create data source from entity definition
|
|
12951
13973
|
*/
|
|
12952
13974
|
createDataSource(options) {
|
|
12953
|
-
const { entity
|
|
13975
|
+
const { entity } = options;
|
|
13976
|
+
const filters = options.filter?.value;
|
|
13977
|
+
const parentFilters = options.filter?.parent;
|
|
12954
13978
|
return new AXDataSource({
|
|
12955
13979
|
byKey: (key) => {
|
|
12956
13980
|
const func = entity.queries?.byKey?.execute;
|
|
@@ -12964,7 +13988,7 @@ class AXPEntityDataSelectorService {
|
|
|
12964
13988
|
this.mergeFilters(e, filters, parentFilters);
|
|
12965
13989
|
return func(e);
|
|
12966
13990
|
},
|
|
12967
|
-
pageSize: 10,
|
|
13991
|
+
pageSize: options.grid?.pageSize ?? 10,
|
|
12968
13992
|
key: 'id',
|
|
12969
13993
|
});
|
|
12970
13994
|
}
|
|
@@ -12972,7 +13996,8 @@ class AXPEntityDataSelectorService {
|
|
|
12972
13996
|
* Create columns configuration from entity definition
|
|
12973
13997
|
*/
|
|
12974
13998
|
createColumns(options) {
|
|
12975
|
-
const { entity
|
|
13999
|
+
const { entity } = options;
|
|
14000
|
+
const columns = options.grid?.columns ?? [];
|
|
12976
14001
|
const { columns: entityColumns = [], properties } = entity;
|
|
12977
14002
|
const visibleProperties = properties.filter(({ schema }) => schema?.visible !== false);
|
|
12978
14003
|
const visiblePropNames = new Set(visibleProperties.map(({ name }) => name));
|
|
@@ -13013,12 +14038,18 @@ class AXPEntityDataSelectorService {
|
|
|
13013
14038
|
dataType: 'string',
|
|
13014
14039
|
interface: {
|
|
13015
14040
|
type: column.showAs.type,
|
|
13016
|
-
options: {
|
|
14041
|
+
options: {
|
|
14042
|
+
...column.showAs.options,
|
|
14043
|
+
popover: { ...column.showAs.options?.['popover'], enabled: false },
|
|
14044
|
+
},
|
|
13017
14045
|
},
|
|
13018
14046
|
},
|
|
13019
14047
|
};
|
|
13020
14048
|
widgetType = column.showAs.type;
|
|
13021
|
-
widgetOptions = {
|
|
14049
|
+
widgetOptions = {
|
|
14050
|
+
...column.showAs.options,
|
|
14051
|
+
popover: { ...column.showAs.options?.['popover'], enabled: false },
|
|
14052
|
+
};
|
|
13022
14053
|
}
|
|
13023
14054
|
else {
|
|
13024
14055
|
// Use entity property configuration - same columns, title, and interface as entity
|
|
@@ -13028,7 +14059,7 @@ class AXPEntityDataSelectorService {
|
|
|
13028
14059
|
widgetOptions = {
|
|
13029
14060
|
...(iface?.options ?? {}),
|
|
13030
14061
|
...(iface?.children?.length && { children: iface.children }),
|
|
13031
|
-
|
|
14062
|
+
popover: { ...iface?.options?.['popover'], enabled: false },
|
|
13032
14063
|
};
|
|
13033
14064
|
}
|
|
13034
14065
|
// Get width from column options (set by middleware)
|
|
@@ -13052,7 +14083,8 @@ class AXPEntityDataSelectorService {
|
|
|
13052
14083
|
* Get searchable fields from entity properties
|
|
13053
14084
|
*/
|
|
13054
14085
|
getSearchFields(options) {
|
|
13055
|
-
const { entity
|
|
14086
|
+
const { entity } = options;
|
|
14087
|
+
const searchFields = options.search?.fields;
|
|
13056
14088
|
const searchableFields = entity.properties
|
|
13057
14089
|
.filter((p) => p.options?.filter?.inline?.enabled)
|
|
13058
14090
|
.map((p) => {
|
|
@@ -13149,89 +14181,26 @@ class AXPEntityDataSelectorService {
|
|
|
13149
14181
|
return normalized;
|
|
13150
14182
|
}
|
|
13151
14183
|
/**
|
|
13152
|
-
* Create category tree data source from category entity key
|
|
14184
|
+
* Create category tree data source from category entity key.
|
|
14185
|
+
* Uses the same {@link AXPCategoryTreeService} as the entity master list category sidebar.
|
|
13153
14186
|
*/
|
|
13154
|
-
async createCategoryTreeDataSource(categoryEntityKey) {
|
|
14187
|
+
async createCategoryTreeDataSource(categoryEntityKey, categoryOptions) {
|
|
13155
14188
|
try {
|
|
13156
|
-
const
|
|
13157
|
-
|
|
13158
|
-
|
|
13159
|
-
|
|
13160
|
-
|
|
13161
|
-
|
|
14189
|
+
const treeConfig = {
|
|
14190
|
+
entityKey: categoryEntityKey,
|
|
14191
|
+
textField: categoryOptions?.textField,
|
|
14192
|
+
valueField: categoryOptions?.valueField,
|
|
14193
|
+
parentKey: categoryOptions?.parentKey,
|
|
14194
|
+
};
|
|
14195
|
+
const treeData = await this.categoryTreeService.initializeCategoryTree(treeConfig);
|
|
14196
|
+
if (!treeData) {
|
|
13162
14197
|
return null;
|
|
13163
14198
|
}
|
|
13164
|
-
const
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
skip: 0,
|
|
13170
|
-
take: 1000,
|
|
13171
|
-
filter: {
|
|
13172
|
-
field: parentKey,
|
|
13173
|
-
operator: { type: 'isNull' },
|
|
13174
|
-
},
|
|
13175
|
-
sort: [{ field: 'title', dir: 'asc' }],
|
|
13176
|
-
});
|
|
13177
|
-
return (result?.items || []).map((item) => ({
|
|
13178
|
-
id: item.id,
|
|
13179
|
-
title: item.title || item.name,
|
|
13180
|
-
description: item.description || '',
|
|
13181
|
-
parentId: item[parentKey],
|
|
13182
|
-
childrenCount: item.childrenCount || 0,
|
|
13183
|
-
}));
|
|
13184
|
-
},
|
|
13185
|
-
loadChildNodes: async (parentId) => {
|
|
13186
|
-
const result = await listQuery({
|
|
13187
|
-
skip: 0,
|
|
13188
|
-
take: 1000,
|
|
13189
|
-
filter: {
|
|
13190
|
-
field: parentKey,
|
|
13191
|
-
operator: { type: 'equal' },
|
|
13192
|
-
value: parentId,
|
|
13193
|
-
},
|
|
13194
|
-
sort: [{ field: 'title', dir: 'asc' }],
|
|
13195
|
-
});
|
|
13196
|
-
return (result?.items || []).map((item) => ({
|
|
13197
|
-
id: item.id,
|
|
13198
|
-
title: item.title || item.name,
|
|
13199
|
-
description: item.description || '',
|
|
13200
|
-
parentId: item[parentKey],
|
|
13201
|
-
childrenCount: item.childrenCount || 0,
|
|
13202
|
-
}));
|
|
13203
|
-
},
|
|
13204
|
-
searchNodes: async (searchValue) => {
|
|
13205
|
-
const result = await listQuery({
|
|
13206
|
-
skip: 0,
|
|
13207
|
-
take: 1000,
|
|
13208
|
-
filter: {
|
|
13209
|
-
filters: [
|
|
13210
|
-
{
|
|
13211
|
-
field: 'title',
|
|
13212
|
-
operator: { type: 'contains' },
|
|
13213
|
-
value: searchValue,
|
|
13214
|
-
},
|
|
13215
|
-
{
|
|
13216
|
-
field: 'name',
|
|
13217
|
-
operator: { type: 'contains' },
|
|
13218
|
-
value: searchValue,
|
|
13219
|
-
},
|
|
13220
|
-
],
|
|
13221
|
-
logic: 'or',
|
|
13222
|
-
},
|
|
13223
|
-
sort: [{ field: 'title', dir: 'asc' }],
|
|
13224
|
-
});
|
|
13225
|
-
return (result?.items || []).map((item) => ({
|
|
13226
|
-
id: item.id,
|
|
13227
|
-
title: item.title || item.name,
|
|
13228
|
-
description: item.description || '',
|
|
13229
|
-
parentId: item[parentKey],
|
|
13230
|
-
childrenCount: item.childrenCount || 0,
|
|
13231
|
-
}));
|
|
13232
|
-
},
|
|
13233
|
-
};
|
|
13234
|
-
return categoryTreeDataSource;
|
|
14199
|
+
const parentKey = treeConfig.parentKey || treeData.categoryEntityDef?.parentKey || 'parentId';
|
|
14200
|
+
treeConfig.parentKey = parentKey;
|
|
14201
|
+
treeConfig.textField = treeConfig.textField ?? 'title';
|
|
14202
|
+
treeConfig.valueField = treeConfig.valueField ?? 'id';
|
|
14203
|
+
return this.buildCategoryTreeDataSource(treeData, treeConfig, parentKey);
|
|
13235
14204
|
}
|
|
13236
14205
|
catch (error) {
|
|
13237
14206
|
console.error('Error creating category tree data source:', error);
|
|
@@ -13239,28 +14208,109 @@ class AXPEntityDataSelectorService {
|
|
|
13239
14208
|
}
|
|
13240
14209
|
}
|
|
13241
14210
|
/**
|
|
13242
|
-
*
|
|
14211
|
+
* Build category tree data source callbacks backed by shared category tree service.
|
|
13243
14212
|
*/
|
|
13244
|
-
|
|
13245
|
-
|
|
13246
|
-
|
|
13247
|
-
|
|
13248
|
-
|
|
13249
|
-
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
}
|
|
14213
|
+
buildCategoryTreeDataSource(treeData, treeConfig, parentKey) {
|
|
14214
|
+
return {
|
|
14215
|
+
loadRootNodes: async () => {
|
|
14216
|
+
const items = await this.categoryTreeService.loadRootCategories(treeData, treeConfig);
|
|
14217
|
+
if (!items) {
|
|
14218
|
+
return [];
|
|
14219
|
+
}
|
|
14220
|
+
return items
|
|
14221
|
+
.filter((item) => isNil(item[parentKey]) || item[parentKey] === '')
|
|
14222
|
+
.map((item) => this.mapCategoryItemToEntity(item, treeConfig, parentKey));
|
|
14223
|
+
},
|
|
14224
|
+
loadChildNodes: async (parentId) => {
|
|
14225
|
+
const targetNode = { id: parentId, title: '', data: {} };
|
|
14226
|
+
const children = await this.categoryTreeService.loadChildren(targetNode, treeData, treeConfig);
|
|
14227
|
+
return children.map((node) => this.mapCategoryTreeNodeToEntity(node, parentKey));
|
|
14228
|
+
},
|
|
14229
|
+
searchNodes: async (searchValue) => {
|
|
14230
|
+
const items = await this.categoryTreeService.searchCategories(searchValue, treeData, treeConfig);
|
|
14231
|
+
if (!items) {
|
|
14232
|
+
return [];
|
|
14233
|
+
}
|
|
14234
|
+
return items.map((item) => this.mapCategoryItemToEntity(item, treeConfig, parentKey));
|
|
14235
|
+
},
|
|
14236
|
+
};
|
|
14237
|
+
}
|
|
14238
|
+
/**
|
|
14239
|
+
* Map a category entity row to {@link AXPCategoryEntity} for the reusable category tree component.
|
|
14240
|
+
*/
|
|
14241
|
+
mapCategoryItemToEntity(item, config, parentKey) {
|
|
14242
|
+
const textField = config.textField ?? 'title';
|
|
14243
|
+
const valueField = config.valueField ?? 'id';
|
|
14244
|
+
let childrenCount = 0;
|
|
14245
|
+
if (typeof item['childrenCount'] === 'number') {
|
|
14246
|
+
childrenCount = item['childrenCount'];
|
|
13255
14247
|
}
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
filters.push(parentFilters);
|
|
14248
|
+
else if (Array.isArray(item['children'])) {
|
|
14249
|
+
childrenCount = item['children'].length;
|
|
13259
14250
|
}
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
14251
|
+
return {
|
|
14252
|
+
id: String(item[valueField] ?? ''),
|
|
14253
|
+
title: this.resolveCategoryText(item[textField] ?? item['name']),
|
|
14254
|
+
description: this.resolveCategoryText(item['description']),
|
|
14255
|
+
parentId: item[parentKey],
|
|
14256
|
+
childrenCount,
|
|
14257
|
+
};
|
|
14258
|
+
}
|
|
14259
|
+
/**
|
|
14260
|
+
* Map a lazy-loaded tree node to {@link AXPCategoryEntity}.
|
|
14261
|
+
*/
|
|
14262
|
+
mapCategoryTreeNodeToEntity(node, parentKey) {
|
|
14263
|
+
const data = node['data'] ?? {};
|
|
14264
|
+
let childrenCount = 0;
|
|
14265
|
+
if (typeof node['childrenCount'] === 'number') {
|
|
14266
|
+
childrenCount = node['childrenCount'];
|
|
14267
|
+
}
|
|
14268
|
+
else if (typeof data['childrenCount'] === 'number') {
|
|
14269
|
+
childrenCount = data['childrenCount'];
|
|
14270
|
+
}
|
|
14271
|
+
return {
|
|
14272
|
+
id: String(node['id'] ?? ''),
|
|
14273
|
+
title: String(node['title'] ?? ''),
|
|
14274
|
+
description: this.resolveCategoryText(data['description']),
|
|
14275
|
+
parentId: data[parentKey],
|
|
14276
|
+
childrenCount,
|
|
14277
|
+
};
|
|
14278
|
+
}
|
|
14279
|
+
/**
|
|
14280
|
+
* Resolve category display text, including multi-language titles.
|
|
14281
|
+
*/
|
|
14282
|
+
resolveCategoryText(value) {
|
|
14283
|
+
if (isNil(value)) {
|
|
14284
|
+
return '';
|
|
14285
|
+
}
|
|
14286
|
+
if (typeof value === 'object') {
|
|
14287
|
+
return resolveMultiLanguageString(value, this.translationService.getActiveLang());
|
|
14288
|
+
}
|
|
14289
|
+
return String(value);
|
|
14290
|
+
}
|
|
14291
|
+
/**
|
|
14292
|
+
* Merge custom and parent filters into data source query
|
|
14293
|
+
*/
|
|
14294
|
+
mergeFilters(request, customFilter, parentFilters) {
|
|
14295
|
+
if (!customFilter && !parentFilters) {
|
|
14296
|
+
return request;
|
|
14297
|
+
}
|
|
14298
|
+
const filters = [];
|
|
14299
|
+
// Add custom filter
|
|
14300
|
+
if (customFilter) {
|
|
14301
|
+
const cleanedFilters = AXPCleanNestedFilters([customFilter]);
|
|
14302
|
+
if (cleanedFilters.length > 0) {
|
|
14303
|
+
filters.push(this.filterOperatorMiddleware.transformFilter(cleanedFilters[0]));
|
|
14304
|
+
}
|
|
14305
|
+
}
|
|
14306
|
+
// Add parent filters
|
|
14307
|
+
if (parentFilters) {
|
|
14308
|
+
filters.push(parentFilters);
|
|
14309
|
+
}
|
|
14310
|
+
// Merge with existing filter
|
|
14311
|
+
if (request.filter) {
|
|
14312
|
+
request.filter = {
|
|
14313
|
+
logic: 'and',
|
|
13264
14314
|
filters: [...[request.filter], ...filters],
|
|
13265
14315
|
};
|
|
13266
14316
|
}
|
|
@@ -13573,11 +14623,13 @@ class AXPLookupWidgetTagboxComponent extends LookupWidgetLookBase {
|
|
|
13573
14623
|
this.placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
13574
14624
|
this.hasClearButton = input(false, ...(ngDevMode ? [{ debugName: "hasClearButton" }] : /* istanbul ignore next */ []));
|
|
13575
14625
|
this.allowCreate = input('none', ...(ngDevMode ? [{ debugName: "allowCreate" }] : /* istanbul ignore next */ []));
|
|
14626
|
+
this.allowUnselect = input(true, ...(ngDevMode ? [{ debugName: "allowUnselect" }] : /* istanbul ignore next */ []));
|
|
13576
14627
|
// Entity and configuration data (for showSelector and searchByValue)
|
|
13577
14628
|
this.entityDef = input.required(...(ngDevMode ? [{ debugName: "entityDef" }] : /* istanbul ignore next */ []));
|
|
13578
14629
|
this.customFilter = input.required(...(ngDevMode ? [{ debugName: "customFilter" }] : /* istanbul ignore next */ []));
|
|
13579
14630
|
this.columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
13580
14631
|
this.parentFilters = input(null, ...(ngDevMode ? [{ debugName: "parentFilters" }] : /* istanbul ignore next */ []));
|
|
14632
|
+
this.rowActions = input(undefined, ...(ngDevMode ? [{ debugName: "rowActions" }] : /* istanbul ignore next */ []));
|
|
13581
14633
|
// Business logic callbacks
|
|
13582
14634
|
this.onSetLoading = input.required(...(ngDevMode ? [{ debugName: "onSetLoading" }] : /* istanbul ignore next */ []));
|
|
13583
14635
|
//#endregion
|
|
@@ -13757,12 +14809,14 @@ class AXPLookupWidgetTagboxComponent extends LookupWidgetLookBase {
|
|
|
13757
14809
|
entity,
|
|
13758
14810
|
title: `${this.translateService.translateSync(entity.formats.plural ?? '')}`,
|
|
13759
14811
|
allowMultiple: this.multiple()(),
|
|
14812
|
+
allowUnselect: this.allowUnselect(),
|
|
13760
14813
|
filters: this.customFilter()(),
|
|
13761
14814
|
parentFilters: this.parentFilters(),
|
|
13762
14815
|
columns: this.columns()(),
|
|
13763
14816
|
allowCreate: this.allowCreate(),
|
|
13764
14817
|
initialSearchTerm: searchTerm,
|
|
13765
14818
|
selectedItemIds: selectedItemIds.length > 0 ? selectedItemIds : undefined,
|
|
14819
|
+
rowActions: this.rowActions(),
|
|
13766
14820
|
});
|
|
13767
14821
|
if (result && 'items' in result) {
|
|
13768
14822
|
this.valueChanged.emit(result.items);
|
|
@@ -13785,7 +14839,7 @@ class AXPLookupWidgetTagboxComponent extends LookupWidgetLookBase {
|
|
|
13785
14839
|
this.clear();
|
|
13786
14840
|
}
|
|
13787
14841
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetTagboxComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
13788
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLookupWidgetTagboxComponent, isStandalone: true, selector: "axp-lookup-widget-tagbox", inputs: { selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: true, transformFunction: null }, displayField: { classPropertyName: "displayField", publicName: "displayField", isSignal: true, isRequired: true, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: true, transformFunction: null }, displayFormat: { classPropertyName: "displayFormat", publicName: "displayFormat", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, validationRules: { classPropertyName: "validationRules", publicName: "validationRules", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hasClearButton: { classPropertyName: "hasClearButton", publicName: "hasClearButton", isSignal: true, isRequired: false, transformFunction: null }, allowCreate: { classPropertyName: "allowCreate", publicName: "allowCreate", isSignal: true, isRequired: false, transformFunction: null }, entityDef: { classPropertyName: "entityDef", publicName: "entityDef", isSignal: true, isRequired: true, transformFunction: null }, customFilter: { classPropertyName: "customFilter", publicName: "customFilter", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, parentFilters: { classPropertyName: "parentFilters", publicName: "parentFilters", isSignal: true, isRequired: false, transformFunction: null }, onSetLoading: { classPropertyName: "onSetLoading", publicName: "onSetLoading", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { valueChanged: "valueChanged" }, providers: [
|
|
14842
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLookupWidgetTagboxComponent, isStandalone: true, selector: "axp-lookup-widget-tagbox", inputs: { selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: true, transformFunction: null }, displayField: { classPropertyName: "displayField", publicName: "displayField", isSignal: true, isRequired: true, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: true, transformFunction: null }, displayFormat: { classPropertyName: "displayFormat", publicName: "displayFormat", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, validationRules: { classPropertyName: "validationRules", publicName: "validationRules", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hasClearButton: { classPropertyName: "hasClearButton", publicName: "hasClearButton", isSignal: true, isRequired: false, transformFunction: null }, allowCreate: { classPropertyName: "allowCreate", publicName: "allowCreate", isSignal: true, isRequired: false, transformFunction: null }, allowUnselect: { classPropertyName: "allowUnselect", publicName: "allowUnselect", isSignal: true, isRequired: false, transformFunction: null }, entityDef: { classPropertyName: "entityDef", publicName: "entityDef", isSignal: true, isRequired: true, transformFunction: null }, customFilter: { classPropertyName: "customFilter", publicName: "customFilter", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, parentFilters: { classPropertyName: "parentFilters", publicName: "parentFilters", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, onSetLoading: { classPropertyName: "onSetLoading", publicName: "onSetLoading", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { valueChanged: "valueChanged" }, providers: [
|
|
13789
14843
|
{
|
|
13790
14844
|
provide: LookupWidgetLookBase,
|
|
13791
14845
|
useExisting: AXPLookupWidgetTagboxComponent,
|
|
@@ -13924,7 +14978,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
13924
14978
|
},
|
|
13925
14979
|
],
|
|
13926
14980
|
}]
|
|
13927
|
-
}], propDecorators: { selectedItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedItems", required: true }] }], displayField: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayField", required: true }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: true }] }], displayFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayFormat", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: true }] }], validationRules: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationRules", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hasClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasClearButton", required: false }] }], allowCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCreate", required: false }] }], entityDef: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityDef", required: true }] }], customFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "customFilter", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], parentFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentFilters", required: false }] }], onSetLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "onSetLoading", required: true }] }], valueChanged: [{ type: i0.Output, args: ["valueChanged"] }], tagBox: [{ type: i0.ViewChild, args: ['tagBoxComponent', { isSignal: true }] }] } });
|
|
14981
|
+
}], propDecorators: { selectedItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedItems", required: true }] }], displayField: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayField", required: true }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: true }] }], displayFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayFormat", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: true }] }], validationRules: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationRules", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hasClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasClearButton", required: false }] }], allowCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCreate", required: false }] }], allowUnselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowUnselect", required: false }] }], entityDef: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityDef", required: true }] }], customFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "customFilter", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], parentFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentFilters", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], onSetLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "onSetLoading", required: true }] }], valueChanged: [{ type: i0.Output, args: ["valueChanged"] }], tagBox: [{ type: i0.ViewChild, args: ['tagBoxComponent', { isSignal: true }] }] } });
|
|
13928
14982
|
|
|
13929
14983
|
class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
13930
14984
|
constructor() {
|
|
@@ -13964,9 +15018,11 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
13964
15018
|
this.placeholder = computed(() => this.options()['placeholder'] ?? '', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
13965
15019
|
this.filterMode = computed(() => this.options()['filterMode'], ...(ngDevMode ? [{ debugName: "filterMode" }] : /* istanbul ignore next */ []));
|
|
13966
15020
|
this.multiple = computed(() => (this.options()['multiple'] ?? false), ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
15021
|
+
this.allowUnselect = computed(() => this.options()['allowUnselect'] ?? true, ...(ngDevMode ? [{ debugName: "allowUnselect" }] : /* istanbul ignore next */ []));
|
|
13967
15022
|
this.look = computed(() => this.options()['look'] ?? 'lookup', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
|
|
13968
15023
|
this.displayField = computed(() => resolveLookupDisplayField(this.entityDef(), { textField: this.textField() }), ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
|
|
13969
15024
|
this.allowCreate = computed(() => this.options()['allowCreate'] ?? 'none', ...(ngDevMode ? [{ debugName: "allowCreate" }] : /* istanbul ignore next */ []));
|
|
15025
|
+
this.selectorRowActions = computed(() => this.options()['selectorRowActions'], ...(ngDevMode ? [{ debugName: "selectorRowActions" }] : /* istanbul ignore next */ []));
|
|
13970
15026
|
this.valueField = computed(() => this.entityDef()?.properties.find((c) => c.name == 'id')?.name ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
|
|
13971
15027
|
this.displayFormat = computed(() => resolveLookupDisplayTemplate(this.entityDef(), {
|
|
13972
15028
|
displayFormat: this.options()['displayFormat'],
|
|
@@ -14247,10 +15303,12 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
14247
15303
|
[validationRules]="validationRules"
|
|
14248
15304
|
[hasClearButton]="hasClearButton()"
|
|
14249
15305
|
[allowCreate]="allowCreate()"
|
|
15306
|
+
[allowUnselect]="allowUnselect()"
|
|
14250
15307
|
[entityDef]="entityDef"
|
|
14251
15308
|
[customFilter]="customFilter"
|
|
14252
15309
|
[columns]="columns"
|
|
14253
15310
|
[parentFilters]="filter"
|
|
15311
|
+
[rowActions]="selectorRowActions()"
|
|
14254
15312
|
[onSetLoading]="setLoading.bind(this)"
|
|
14255
15313
|
(valueChanged)="handleComponentValueChanged($event)"
|
|
14256
15314
|
/>
|
|
@@ -14271,7 +15329,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
|
|
|
14271
15329
|
}
|
|
14272
15330
|
</div>
|
|
14273
15331
|
}
|
|
14274
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: AXDecoratorModule }, { kind: "component", type: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPLookupWidgetSelectComponent, selector: "axp-lookup-widget-select", inputs: ["entityDef", "customFilter", "selectedItems", "displayField", "valueField", "disabled", "multiple", "validationRules", "placeholder", "hasClearButton", "allowCreate", "showItemTooltip", "isItemTruncated"], outputs: ["valueChanged"] }, { kind: "component", type: AXPLookupWidgetTagboxComponent, selector: "axp-lookup-widget-tagbox", inputs: ["selectedItems", "displayField", "valueField", "displayFormat", "disabled", "multiple", "isLoading", "validationRules", "placeholder", "hasClearButton", "allowCreate", "entityDef", "customFilter", "columns", "parentFilters", "onSetLoading"], outputs: ["valueChanged"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15332
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: AXDecoratorModule }, { kind: "component", type: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPLookupWidgetSelectComponent, selector: "axp-lookup-widget-select", inputs: ["entityDef", "customFilter", "selectedItems", "displayField", "valueField", "disabled", "multiple", "validationRules", "placeholder", "hasClearButton", "allowCreate", "showItemTooltip", "isItemTruncated"], outputs: ["valueChanged"] }, { kind: "component", type: AXPLookupWidgetTagboxComponent, selector: "axp-lookup-widget-tagbox", inputs: ["selectedItems", "displayField", "valueField", "displayFormat", "disabled", "multiple", "isLoading", "validationRules", "placeholder", "hasClearButton", "allowCreate", "allowUnselect", "entityDef", "customFilter", "columns", "parentFilters", "rowActions", "onSetLoading"], outputs: ["valueChanged"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
14275
15333
|
}
|
|
14276
15334
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetEditComponent, decorators: [{
|
|
14277
15335
|
type: Component,
|
|
@@ -14314,10 +15372,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
14314
15372
|
[validationRules]="validationRules"
|
|
14315
15373
|
[hasClearButton]="hasClearButton()"
|
|
14316
15374
|
[allowCreate]="allowCreate()"
|
|
15375
|
+
[allowUnselect]="allowUnselect()"
|
|
14317
15376
|
[entityDef]="entityDef"
|
|
14318
15377
|
[customFilter]="customFilter"
|
|
14319
15378
|
[columns]="columns"
|
|
14320
15379
|
[parentFilters]="filter"
|
|
15380
|
+
[rowActions]="selectorRowActions()"
|
|
14321
15381
|
[onSetLoading]="setLoading.bind(this)"
|
|
14322
15382
|
(valueChanged)="handleComponentValueChanged($event)"
|
|
14323
15383
|
/>
|
|
@@ -14393,6 +15453,10 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14393
15453
|
return this.columnResolve()?.strategy ?? 'hydrated';
|
|
14394
15454
|
}, ...(ngDevMode ? [{ debugName: "resolveStrategy" }] : /* istanbul ignore next */ []));
|
|
14395
15455
|
this.isHydratedStrategy = computed(() => this.resolveStrategy() === 'hydrated', ...(ngDevMode ? [{ debugName: "isHydratedStrategy" }] : /* istanbul ignore next */ []));
|
|
15456
|
+
this.detailPopoverEnabled = computed(() => {
|
|
15457
|
+
const popover = this.options.popover;
|
|
15458
|
+
return popover?.enabled !== false;
|
|
15459
|
+
}, ...(ngDevMode ? [{ debugName: "detailPopoverEnabled" }] : /* istanbul ignore next */ []));
|
|
14396
15460
|
//#endregion
|
|
14397
15461
|
//#region ---- Signals ----
|
|
14398
15462
|
this.isMorePopoverOpen = signal(false, ...(ngDevMode ? [{ debugName: "isMorePopoverOpen" }] : /* istanbul ignore next */ []));
|
|
@@ -14519,8 +15583,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14519
15583
|
return;
|
|
14520
15584
|
}
|
|
14521
15585
|
const values = castArray(raw).filter((value) => value != null && value !== '');
|
|
14522
|
-
const needsHydrate = values.length > 0 &&
|
|
14523
|
-
values.every((value) => typeof value !== 'object' && this.isLikelyEntityId(value));
|
|
15586
|
+
const needsHydrate = values.length > 0 && values.every((value) => typeof value !== 'object' && this.isLikelyEntityId(value));
|
|
14524
15587
|
if (!needsHydrate) {
|
|
14525
15588
|
this.hydratedDisplayItems.set(null);
|
|
14526
15589
|
return;
|
|
@@ -14549,7 +15612,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14549
15612
|
}
|
|
14550
15613
|
}
|
|
14551
15614
|
async showItemDetail(item, index) {
|
|
14552
|
-
if (this.
|
|
15615
|
+
if (!this.detailPopoverEnabled()) {
|
|
14553
15616
|
return;
|
|
14554
15617
|
}
|
|
14555
15618
|
this.selectedItemIndex.set(index);
|
|
@@ -14581,9 +15644,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14581
15644
|
let resolvedTitle = '';
|
|
14582
15645
|
if (headerItem != null && this.hasDisplayTextOnLookupValue(headerItem)) {
|
|
14583
15646
|
resolvedTitle =
|
|
14584
|
-
typeof headerItem === 'object'
|
|
14585
|
-
? this.renderLookupLabel(headerItem)
|
|
14586
|
-
: this.formatDisplayValue(headerItem);
|
|
15647
|
+
typeof headerItem === 'object' ? this.renderLookupLabel(headerItem) : this.formatDisplayValue(headerItem);
|
|
14587
15648
|
}
|
|
14588
15649
|
else if (!this.isMultiValueLookup()) {
|
|
14589
15650
|
const rowText = this.getRowDisplayTextFromTextField();
|
|
@@ -14608,7 +15669,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14608
15669
|
});
|
|
14609
15670
|
}
|
|
14610
15671
|
handleItemClick(index) {
|
|
14611
|
-
if (this.
|
|
15672
|
+
if (!this.detailPopoverEnabled()) {
|
|
14612
15673
|
return;
|
|
14613
15674
|
}
|
|
14614
15675
|
const items = this.allItems();
|
|
@@ -14617,7 +15678,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14617
15678
|
}
|
|
14618
15679
|
}
|
|
14619
15680
|
handleResolvedItemClick(index) {
|
|
14620
|
-
if (this.
|
|
15681
|
+
if (!this.detailPopoverEnabled()) {
|
|
14621
15682
|
return;
|
|
14622
15683
|
}
|
|
14623
15684
|
const items = this.resolvedPopoverItems();
|
|
@@ -14643,9 +15704,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14643
15704
|
return values.length > 0 && values.every((value) => this.hasDisplayTextOnLookupValue(value));
|
|
14644
15705
|
}
|
|
14645
15706
|
const rowText = this.getRowDisplayTextFromTextField();
|
|
14646
|
-
if (!isNil(rowText) &&
|
|
14647
|
-
rowText !== '' &&
|
|
14648
|
-
(typeof rowText !== 'string' || !this.isLikelyEntityId(rowText))) {
|
|
15707
|
+
if (!isNil(rowText) && rowText !== '' && (typeof rowText !== 'string' || !this.isLikelyEntityId(rowText))) {
|
|
14649
15708
|
return true;
|
|
14650
15709
|
}
|
|
14651
15710
|
const raw = this.rawValue;
|
|
@@ -14991,11 +16050,11 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
14991
16050
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value) || /^\d+$/.test(value);
|
|
14992
16051
|
}
|
|
14993
16052
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14994
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "lazyTrigger", first: true, predicate: ["lazyTrigger"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@if (isHydratedStrategy()) {\n<div class=\"ax-relative ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @for (item of visibleItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"
|
|
16053
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "lazyTrigger", first: true, predicate: ["lazyTrigger"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@if (isHydratedStrategy()) {\n<div class=\"ax-relative ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @for (item of visibleItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\n (click)=\"handleItemClick($index)\"\n >\n {{ label }}\n </span>\n @if ($index < visibleItems().length - 1) {\n <span class=\"ax-text-muted\">\u2022</span>\n }\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-end-0 ax-flex ax-h-full ax-cursor-pointer ax-items-center ax-px-1 hover:ax-primary-lighter\"\n (click)=\"showMoreItems(); $event.stopPropagation()\"\n #moreButton\n >\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n} @else {\n<div class=\"ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @if (displayCount() > 0) {\n <span\n class=\"ax-cursor-pointer ax-text-primary hover:ax-underline\"\n (click)=\"openLazyPopover(); $event.stopPropagation()\"\n #lazyTrigger\n >\n {{ summaryLabel() }}\n </span>\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n</div>\n}\n\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-min-w-[280px] ax-rounded-lg ax-border ax-p-4 ax-shadow-lg\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">{{ popoverHeader() }}</h3>\n </div>\n\n @if (!isHydratedStrategy() && resolveStatus() === 'loading') {\n <div class=\"ax-flex ax-min-h-[120px] ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else if (!isHydratedStrategy() && resolveStatus() === 'error') {\n <div class=\"ax-text-danger ax-text-sm\">{{ resolveError() }}</div>\n } @else {\n <div class=\"ax-flex ax-max-h-64 ax-flex-col ax-gap-3\">\n @for (item of popoverListItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\n (click)=\"handlePopoverItemClick($index)\"\n >\n {{ label }}\n </span>\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n </div>\n }\n </div>\n</ax-popover>\n", dependencies: [{ kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
14995
16054
|
}
|
|
14996
16055
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetColumnComponent, decorators: [{
|
|
14997
16056
|
type: Component,
|
|
14998
|
-
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXLoadingModule, AXPopoverModule], inputs: ['rawValue', 'rowData'], template: "@if (isHydratedStrategy()) {\n<div class=\"ax-relative ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @for (item of visibleItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"
|
|
16057
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXLoadingModule, AXPopoverModule], inputs: ['rawValue', 'rowData'], template: "@if (isHydratedStrategy()) {\n<div class=\"ax-relative ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @for (item of visibleItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\n (click)=\"handleItemClick($index)\"\n >\n {{ label }}\n </span>\n @if ($index < visibleItems().length - 1) {\n <span class=\"ax-text-muted\">\u2022</span>\n }\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-end-0 ax-flex ax-h-full ax-cursor-pointer ax-items-center ax-px-1 hover:ax-primary-lighter\"\n (click)=\"showMoreItems(); $event.stopPropagation()\"\n #moreButton\n >\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n} @else {\n<div class=\"ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @if (displayCount() > 0) {\n <span\n class=\"ax-cursor-pointer ax-text-primary hover:ax-underline\"\n (click)=\"openLazyPopover(); $event.stopPropagation()\"\n #lazyTrigger\n >\n {{ summaryLabel() }}\n </span>\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n</div>\n}\n\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-min-w-[280px] ax-rounded-lg ax-border ax-p-4 ax-shadow-lg\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">{{ popoverHeader() }}</h3>\n </div>\n\n @if (!isHydratedStrategy() && resolveStatus() === 'loading') {\n <div class=\"ax-flex ax-min-h-[120px] ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else if (!isHydratedStrategy() && resolveStatus() === 'error') {\n <div class=\"ax-text-danger ax-text-sm\">{{ resolveError() }}</div>\n } @else {\n <div class=\"ax-flex ax-max-h-64 ax-flex-col ax-gap-3\">\n @for (item of popoverListItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\n (click)=\"handlePopoverItemClick($index)\"\n >\n {{ label }}\n </span>\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n </div>\n }\n </div>\n</ax-popover>\n" }]
|
|
14999
16058
|
}], ctorParameters: () => [], propDecorators: { moreButton: [{ type: i0.ViewChild, args: ['moreButton', { isSignal: true }] }], lazyTrigger: [{ type: i0.ViewChild, args: ['lazyTrigger', { isSignal: true }] }], morePopover: [{ type: i0.ViewChild, args: ['morePopover', { isSignal: true }] }] } });
|
|
15000
16059
|
|
|
15001
16060
|
var lookupWidgetColumn_component = /*#__PURE__*/Object.freeze({
|
|
@@ -15044,6 +16103,13 @@ const AXPLookupWidget = {
|
|
|
15044
16103
|
visible: true,
|
|
15045
16104
|
},
|
|
15046
16105
|
AXP_ALLOW_MULTIPLE_PROPERTY,
|
|
16106
|
+
createBooleanProperty({
|
|
16107
|
+
name: 'popoverEnabled',
|
|
16108
|
+
title: 'Entity Detail Popover',
|
|
16109
|
+
path: 'options.popover.enabled',
|
|
16110
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
16111
|
+
defaultValue: true,
|
|
16112
|
+
}),
|
|
15047
16113
|
{
|
|
15048
16114
|
name: 'displayFormat',
|
|
15049
16115
|
title: 'Display Format',
|
|
@@ -16350,105 +17416,1921 @@ class AXPMultiSourceSelectorWidgetColumnComponent extends AXPColumnWidgetCompone
|
|
|
16350
17416
|
const def = await this.definitionService.getDefinition(this.providerName);
|
|
16351
17417
|
this.definition.set(def);
|
|
16352
17418
|
}
|
|
16353
|
-
catch (error) {
|
|
16354
|
-
console.error(`[MultiSourceSelectorColumn] Error loading definition '${this.providerName}':`, error);
|
|
17419
|
+
catch (error) {
|
|
17420
|
+
console.error(`[MultiSourceSelectorColumn] Error loading definition '${this.providerName}':`, error);
|
|
17421
|
+
}
|
|
17422
|
+
}
|
|
17423
|
+
extractDisplayItem(ref) {
|
|
17424
|
+
if (!ref) {
|
|
17425
|
+
return null;
|
|
17426
|
+
}
|
|
17427
|
+
const def = this.definition();
|
|
17428
|
+
// If ref is already AXPMultiSourceRef format
|
|
17429
|
+
if (ref.sourceKey && ref.displayName) {
|
|
17430
|
+
return {
|
|
17431
|
+
id: ref.refId || ref.sourceKey,
|
|
17432
|
+
text: ref.displayName,
|
|
17433
|
+
};
|
|
17434
|
+
}
|
|
17435
|
+
// If ref is object with id
|
|
17436
|
+
if (typeof ref === 'object' && ref.id) {
|
|
17437
|
+
const sourceKey = ref.sourceKey;
|
|
17438
|
+
if (sourceKey && def) {
|
|
17439
|
+
const source = def.sources.find((s) => s.key === sourceKey);
|
|
17440
|
+
if (source) {
|
|
17441
|
+
const displayText = this.getDisplayText(ref, source);
|
|
17442
|
+
return {
|
|
17443
|
+
id: ref.id,
|
|
17444
|
+
text: displayText,
|
|
17445
|
+
};
|
|
17446
|
+
}
|
|
17447
|
+
}
|
|
17448
|
+
// Fallback
|
|
17449
|
+
const displayText = ref.displayName || ref.title || ref.id || String(ref);
|
|
17450
|
+
return {
|
|
17451
|
+
id: ref.id,
|
|
17452
|
+
text: displayText,
|
|
17453
|
+
};
|
|
17454
|
+
}
|
|
17455
|
+
// If ref is primitive
|
|
17456
|
+
return {
|
|
17457
|
+
id: String(ref),
|
|
17458
|
+
text: String(ref),
|
|
17459
|
+
};
|
|
17460
|
+
}
|
|
17461
|
+
getDisplayText(item, source) {
|
|
17462
|
+
if (!item) {
|
|
17463
|
+
return '';
|
|
17464
|
+
}
|
|
17465
|
+
const template = source.displayFormat || this.displayFormat();
|
|
17466
|
+
if (template) {
|
|
17467
|
+
const formatted = this.formatService.format(template, 'string', item);
|
|
17468
|
+
if (formatted) {
|
|
17469
|
+
return formatted;
|
|
17470
|
+
}
|
|
17471
|
+
}
|
|
17472
|
+
// Fallback to common display fields
|
|
17473
|
+
return get(item, 'displayName') || get(item, 'title') || get(item, 'name') || String(item.id || item);
|
|
17474
|
+
}
|
|
17475
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPMultiSourceSelectorWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
17476
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPMultiSourceSelectorWidgetColumnComponent, isStandalone: true, selector: "axp-multi-source-selector-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, usesInheritance: true, ngImport: i0, template: `
|
|
17477
|
+
@if (displayItems().length > 0) {
|
|
17478
|
+
<div class="ax-flex ax-flex-wrap ax-gap-1">
|
|
17479
|
+
@for (item of visibleItems(); track item.id) {
|
|
17480
|
+
<ax-badge class="ax-p-0.5 ax-accent1" [text]="item.text"></ax-badge>
|
|
17481
|
+
}
|
|
17482
|
+
@if (hasMoreItems()) {
|
|
17483
|
+
<ax-badge class="ax-p-0.5 ax-accent2" [text]="'+' + remainingItemsCount()"></ax-badge>
|
|
17484
|
+
}
|
|
17485
|
+
</div>
|
|
17486
|
+
} @else {
|
|
17487
|
+
<span class="ax-text-muted">---</span>
|
|
17488
|
+
}
|
|
17489
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$1.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
17490
|
+
}
|
|
17491
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPMultiSourceSelectorWidgetColumnComponent, decorators: [{
|
|
17492
|
+
type: Component,
|
|
17493
|
+
args: [{
|
|
17494
|
+
selector: 'axp-multi-source-selector-widget-column',
|
|
17495
|
+
template: `
|
|
17496
|
+
@if (displayItems().length > 0) {
|
|
17497
|
+
<div class="ax-flex ax-flex-wrap ax-gap-1">
|
|
17498
|
+
@for (item of visibleItems(); track item.id) {
|
|
17499
|
+
<ax-badge class="ax-p-0.5 ax-accent1" [text]="item.text"></ax-badge>
|
|
17500
|
+
}
|
|
17501
|
+
@if (hasMoreItems()) {
|
|
17502
|
+
<ax-badge class="ax-p-0.5 ax-accent2" [text]="'+' + remainingItemsCount()"></ax-badge>
|
|
17503
|
+
}
|
|
17504
|
+
</div>
|
|
17505
|
+
} @else {
|
|
17506
|
+
<span class="ax-text-muted">---</span>
|
|
17507
|
+
}
|
|
17508
|
+
`,
|
|
17509
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
17510
|
+
imports: [AXBadgeModule],
|
|
17511
|
+
inputs: ['rawValue', 'rowData'],
|
|
17512
|
+
}]
|
|
17513
|
+
}], ctorParameters: () => [] });
|
|
17514
|
+
|
|
17515
|
+
var multiSourceSelectorWidgetColumn_component = /*#__PURE__*/Object.freeze({
|
|
17516
|
+
__proto__: null,
|
|
17517
|
+
AXPMultiSourceSelectorWidgetColumnComponent: AXPMultiSourceSelectorWidgetColumnComponent
|
|
17518
|
+
});
|
|
17519
|
+
|
|
17520
|
+
const FileUploaderWebhookKeys = {
|
|
17521
|
+
Actions: 'file-uploader.actions',
|
|
17522
|
+
Configure: 'file-uploader.configure',
|
|
17523
|
+
BeforeFilesAdded: 'file-uploader.beforeFilesAdded',
|
|
17524
|
+
AfterFilesAdded: 'file-uploader.afterFilesAdded',
|
|
17525
|
+
FileItem: {
|
|
17526
|
+
Actions: 'file-uploader.fileItem.actions',
|
|
17527
|
+
BeforeDownload: 'file-uploader.fileItem.before-download',
|
|
17528
|
+
AfterDownload: 'file-uploader.fileItem.after-download',
|
|
17529
|
+
AfterEdit: 'file-uploader.fileItem.after-edit',
|
|
17530
|
+
AfterRemove: 'file-uploader.fileItem.after-remove',
|
|
17531
|
+
DownloadReference: 'file-uploader.fileItem.download-reference',
|
|
17532
|
+
},
|
|
17533
|
+
EditDialog: {
|
|
17534
|
+
Params: {
|
|
17535
|
+
BeforeRename: 'file-uploader.edit-dialog.params.before-rename',
|
|
17536
|
+
AfterRename: 'file-uploader.edit-dialog.params.after-rename',
|
|
17537
|
+
},
|
|
17538
|
+
Groups: {
|
|
17539
|
+
BeforeForm: 'file-uploader.edit-dialog.groups.before-form',
|
|
17540
|
+
AfterForm: 'file-uploader.edit-dialog.groups.after-form',
|
|
17541
|
+
},
|
|
17542
|
+
},
|
|
17543
|
+
};
|
|
17544
|
+
|
|
17545
|
+
class AXPEditFileUploaderCommand {
|
|
17546
|
+
constructor() {
|
|
17547
|
+
//#region ---- Services & Dependencies ----
|
|
17548
|
+
this.layoutBuilder = inject(AXPLayoutBuilderService);
|
|
17549
|
+
this.translationService = inject(AXTranslationService);
|
|
17550
|
+
this.hooks = inject(AXPHookService);
|
|
17551
|
+
}
|
|
17552
|
+
//#endregion
|
|
17553
|
+
//#region ---- Command Execution ----
|
|
17554
|
+
async execute(input) {
|
|
17555
|
+
if (input.file.readOnly === true && !input.isNewFile) {
|
|
17556
|
+
const text = await this.translationService.translateAsync('@general:terms.common.readonly');
|
|
17557
|
+
return {
|
|
17558
|
+
success: false,
|
|
17559
|
+
message: { text: `${text}.` },
|
|
17560
|
+
};
|
|
17561
|
+
}
|
|
17562
|
+
try {
|
|
17563
|
+
const updatedFile = await this.showEditDialog(input.file, input.plugins ?? [], input.excludePlugins ?? [], input.enableTitleDescription ?? false, input.isNewFile ?? false);
|
|
17564
|
+
return {
|
|
17565
|
+
success: true,
|
|
17566
|
+
data: updatedFile
|
|
17567
|
+
};
|
|
17568
|
+
}
|
|
17569
|
+
catch (error) {
|
|
17570
|
+
return {
|
|
17571
|
+
success: false,
|
|
17572
|
+
message: {
|
|
17573
|
+
text: error instanceof Error ? error.message : 'Failed to edit file'
|
|
17574
|
+
}
|
|
17575
|
+
};
|
|
17576
|
+
}
|
|
17577
|
+
}
|
|
17578
|
+
//#endregion
|
|
17579
|
+
//#region ---- Dialog Logic ----
|
|
17580
|
+
async showEditDialog(file, plugins, excludePlugins, enableTitleDescription, isNewFile) {
|
|
17581
|
+
const titleKey = isNewFile ? '@general:widgets.file-uploader.edit-dialog.add-title' : '@general:widgets.file-uploader.edit-dialog.title';
|
|
17582
|
+
const title = await this.translationService.translateAsync(titleKey);
|
|
17583
|
+
const nameLabel = await this.translationService.translateAsync('@general:terms.common.name');
|
|
17584
|
+
const namePlaceholder = await this.translationService.translateAsync('@general:terms.common.name');
|
|
17585
|
+
const titleLabel = await this.translationService.translateAsync('@general:terms.common.title');
|
|
17586
|
+
const descriptionLabel = await this.translationService.translateAsync('@general:terms.common.description');
|
|
17587
|
+
const submitLabel = await this.translationService.translateAsync('@general:actions.submit.title');
|
|
17588
|
+
// Hooks: parameter insertion points
|
|
17589
|
+
const beforeRenamePayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Params.BeforeRename, {
|
|
17590
|
+
file: file,
|
|
17591
|
+
plugins,
|
|
17592
|
+
excludePlugins,
|
|
17593
|
+
items: [],
|
|
17594
|
+
});
|
|
17595
|
+
const afterRenamePayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Params.AfterRename, {
|
|
17596
|
+
file: file,
|
|
17597
|
+
plugins,
|
|
17598
|
+
excludePlugins,
|
|
17599
|
+
items: [],
|
|
17600
|
+
});
|
|
17601
|
+
// Hooks: group insertion points around 'form'
|
|
17602
|
+
const groupsBeforePayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Groups.BeforeForm, {
|
|
17603
|
+
file: file,
|
|
17604
|
+
plugins,
|
|
17605
|
+
excludePlugins,
|
|
17606
|
+
groups: [],
|
|
17607
|
+
});
|
|
17608
|
+
const groupsAfterPayload = await this.hooks.runAsync(FileUploaderWebhookKeys.EditDialog.Groups.AfterForm, {
|
|
17609
|
+
file: file,
|
|
17610
|
+
plugins,
|
|
17611
|
+
excludePlugins,
|
|
17612
|
+
groups: [],
|
|
17613
|
+
});
|
|
17614
|
+
const dialogRef = await this.layoutBuilder
|
|
17615
|
+
.create()
|
|
17616
|
+
.dialog(dialog => {
|
|
17617
|
+
dialog
|
|
17618
|
+
.setTitle(title)
|
|
17619
|
+
.setContext({ ...file })
|
|
17620
|
+
.content(contentBuilder => {
|
|
17621
|
+
// Add groups before form
|
|
17622
|
+
this.buildGroups(contentBuilder, groupsBeforePayload?.groups ?? []);
|
|
17623
|
+
// Main form container with rename field and hook items
|
|
17624
|
+
contentBuilder.flex(formFlex => {
|
|
17625
|
+
formFlex
|
|
17626
|
+
.setDirection('column')
|
|
17627
|
+
.setGap('16px');
|
|
17628
|
+
// Add items before rename
|
|
17629
|
+
this.buildFormFields(formFlex, beforeRenamePayload?.items ?? []);
|
|
17630
|
+
// Name field (label "Name" when enableTitleDescription, else "Rename")
|
|
17631
|
+
formFlex.formField(nameLabel, field => {
|
|
17632
|
+
field.path('name');
|
|
17633
|
+
field.textBox({
|
|
17634
|
+
placeholder: namePlaceholder,
|
|
17635
|
+
validations: [
|
|
17636
|
+
{ rule: 'maxLength', options: { value: 256 } }
|
|
17637
|
+
]
|
|
17638
|
+
});
|
|
17639
|
+
});
|
|
17640
|
+
// Title and description fields when enableTitleDescription is true
|
|
17641
|
+
if (enableTitleDescription) {
|
|
17642
|
+
formFlex.formField(titleLabel, field => {
|
|
17643
|
+
field.path('title');
|
|
17644
|
+
field.textBox({
|
|
17645
|
+
placeholder: titleLabel,
|
|
17646
|
+
validations: [
|
|
17647
|
+
{ rule: 'maxLength', options: { value: 256 } }
|
|
17648
|
+
]
|
|
17649
|
+
});
|
|
17650
|
+
});
|
|
17651
|
+
formFlex.formField(descriptionLabel, field => {
|
|
17652
|
+
field.path('description');
|
|
17653
|
+
field.largeTextBox({
|
|
17654
|
+
placeholder: descriptionLabel,
|
|
17655
|
+
validations: [
|
|
17656
|
+
{ rule: 'maxLength', options: { value: 1024 } }
|
|
17657
|
+
]
|
|
17658
|
+
});
|
|
17659
|
+
});
|
|
17660
|
+
}
|
|
17661
|
+
// Add items after rename
|
|
17662
|
+
this.buildFormFields(formFlex, afterRenamePayload?.items ?? []);
|
|
17663
|
+
});
|
|
17664
|
+
// Add groups after form
|
|
17665
|
+
this.buildGroups(contentBuilder, groupsAfterPayload?.groups ?? []);
|
|
17666
|
+
})
|
|
17667
|
+
.setCloseButton(true)
|
|
17668
|
+
.setActions(actions => {
|
|
17669
|
+
actions.custom({
|
|
17670
|
+
title: submitLabel,
|
|
17671
|
+
icon: 'fa-check',
|
|
17672
|
+
color: 'primary',
|
|
17673
|
+
command: { name: 'submit', options: { validate: true } }
|
|
17674
|
+
});
|
|
17675
|
+
});
|
|
17676
|
+
})
|
|
17677
|
+
.show();
|
|
17678
|
+
const result = dialogRef.context();
|
|
17679
|
+
dialogRef.close();
|
|
17680
|
+
return result;
|
|
17681
|
+
}
|
|
17682
|
+
//#endregion
|
|
17683
|
+
//#region ---- Helper Methods ----
|
|
17684
|
+
/**
|
|
17685
|
+
* Build form fields from hook items (old format parameters)
|
|
17686
|
+
*/
|
|
17687
|
+
buildFormFields(builder, items) {
|
|
17688
|
+
for (const item of items) {
|
|
17689
|
+
if (!item || !item.path) {
|
|
17690
|
+
continue;
|
|
17691
|
+
}
|
|
17692
|
+
const label = item.title || item.path;
|
|
17693
|
+
const widgetType = item.widget?.type || 'text-editor';
|
|
17694
|
+
const widgetOptions = item.widget?.options || {};
|
|
17695
|
+
builder.formField(label, field => {
|
|
17696
|
+
field.path(item.path);
|
|
17697
|
+
// Apply mode if specified
|
|
17698
|
+
if (item.mode) {
|
|
17699
|
+
field.mode(item.mode);
|
|
17700
|
+
}
|
|
17701
|
+
// Apply visibility if specified
|
|
17702
|
+
if (item.visible !== undefined) {
|
|
17703
|
+
field.visible(item.visible);
|
|
17704
|
+
}
|
|
17705
|
+
// Apply readonly if specified
|
|
17706
|
+
if (item.readonly !== undefined) {
|
|
17707
|
+
field.readonly(item.readonly);
|
|
17708
|
+
}
|
|
17709
|
+
// Apply disabled if specified
|
|
17710
|
+
if (item.disabled !== undefined) {
|
|
17711
|
+
field.disabled(item.disabled);
|
|
17712
|
+
}
|
|
17713
|
+
// Convert widget based on type
|
|
17714
|
+
this.applyWidget(field, widgetType, widgetOptions, item.validations);
|
|
17715
|
+
});
|
|
17716
|
+
}
|
|
17717
|
+
}
|
|
17718
|
+
/**
|
|
17719
|
+
* Build groups/containers from hook groups (old format groups)
|
|
17720
|
+
*/
|
|
17721
|
+
buildGroups(containerBuilder, groups) {
|
|
17722
|
+
for (const group of groups) {
|
|
17723
|
+
if (!group) {
|
|
17724
|
+
continue;
|
|
17725
|
+
}
|
|
17726
|
+
// Use fieldset for groups with title, otherwise use flex
|
|
17727
|
+
if (group.title) {
|
|
17728
|
+
containerBuilder.fieldset(fieldset => {
|
|
17729
|
+
fieldset.setTitle(group.title);
|
|
17730
|
+
if (group.description) {
|
|
17731
|
+
fieldset.setDescription(group.description);
|
|
17732
|
+
}
|
|
17733
|
+
if (group.mode) {
|
|
17734
|
+
fieldset.mode(group.mode);
|
|
17735
|
+
}
|
|
17736
|
+
if (group.cols) {
|
|
17737
|
+
fieldset.setCols(group.cols);
|
|
17738
|
+
}
|
|
17739
|
+
// Build form fields from group parameters
|
|
17740
|
+
if (group.parameters && Array.isArray(group.parameters)) {
|
|
17741
|
+
this.buildFormFields(fieldset, group.parameters);
|
|
17742
|
+
}
|
|
17743
|
+
});
|
|
17744
|
+
}
|
|
17745
|
+
else {
|
|
17746
|
+
containerBuilder.flex(flex => {
|
|
17747
|
+
flex.setDirection('column');
|
|
17748
|
+
flex.setGap('16px');
|
|
17749
|
+
if (group.mode) {
|
|
17750
|
+
flex.mode(group.mode);
|
|
17751
|
+
}
|
|
17752
|
+
// Build form fields from group parameters
|
|
17753
|
+
if (group.parameters && Array.isArray(group.parameters)) {
|
|
17754
|
+
this.buildFormFields(flex, group.parameters);
|
|
17755
|
+
}
|
|
17756
|
+
});
|
|
17757
|
+
}
|
|
17758
|
+
}
|
|
17759
|
+
}
|
|
17760
|
+
/**
|
|
17761
|
+
* Apply widget configuration to field based on widget type
|
|
17762
|
+
*/
|
|
17763
|
+
applyWidget(field, widgetType, options, validations) {
|
|
17764
|
+
// Convert validations from old format if needed
|
|
17765
|
+
const fieldValidations = validations || options.validations || [];
|
|
17766
|
+
// Handle different widget types
|
|
17767
|
+
switch (widgetType) {
|
|
17768
|
+
case 'text-editor':
|
|
17769
|
+
case 'text-box':
|
|
17770
|
+
field.textBox({
|
|
17771
|
+
...options,
|
|
17772
|
+
validations: fieldValidations
|
|
17773
|
+
});
|
|
17774
|
+
break;
|
|
17775
|
+
case 'large-text-editor':
|
|
17776
|
+
case 'large-text-box':
|
|
17777
|
+
field.largeTextBox({
|
|
17778
|
+
...options,
|
|
17779
|
+
validations: fieldValidations
|
|
17780
|
+
});
|
|
17781
|
+
break;
|
|
17782
|
+
case 'rich-text-editor':
|
|
17783
|
+
case 'rich-text':
|
|
17784
|
+
field.richText({
|
|
17785
|
+
...options,
|
|
17786
|
+
validations: fieldValidations
|
|
17787
|
+
});
|
|
17788
|
+
break;
|
|
17789
|
+
case 'number-editor':
|
|
17790
|
+
case 'number-box':
|
|
17791
|
+
field.numberBox({
|
|
17792
|
+
...options,
|
|
17793
|
+
validations: fieldValidations
|
|
17794
|
+
});
|
|
17795
|
+
break;
|
|
17796
|
+
case 'date-time-editor':
|
|
17797
|
+
case 'date-time-box':
|
|
17798
|
+
field.dateTimeBox({
|
|
17799
|
+
...options,
|
|
17800
|
+
validations: fieldValidations
|
|
17801
|
+
});
|
|
17802
|
+
break;
|
|
17803
|
+
case 'select-box':
|
|
17804
|
+
case 'select-editor':
|
|
17805
|
+
field.selectBox({
|
|
17806
|
+
...options,
|
|
17807
|
+
validations: fieldValidations
|
|
17808
|
+
});
|
|
17809
|
+
break;
|
|
17810
|
+
case 'lookup-box':
|
|
17811
|
+
case 'lookup-editor':
|
|
17812
|
+
field.lookupBox({
|
|
17813
|
+
...options,
|
|
17814
|
+
validations: fieldValidations
|
|
17815
|
+
});
|
|
17816
|
+
break;
|
|
17817
|
+
case 'toggle-switch':
|
|
17818
|
+
case 'boolean-editor':
|
|
17819
|
+
field.toggleSwitch({
|
|
17820
|
+
...options,
|
|
17821
|
+
validations: fieldValidations
|
|
17822
|
+
});
|
|
17823
|
+
break;
|
|
17824
|
+
default:
|
|
17825
|
+
// For custom widgets or unknown types, use customWidget
|
|
17826
|
+
field.customWidget(widgetType, {
|
|
17827
|
+
...options,
|
|
17828
|
+
validations: fieldValidations
|
|
17829
|
+
});
|
|
17830
|
+
break;
|
|
17831
|
+
}
|
|
17832
|
+
}
|
|
17833
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEditFileUploaderCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
17834
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEditFileUploaderCommand, providedIn: 'root' }); }
|
|
17835
|
+
}
|
|
17836
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEditFileUploaderCommand, decorators: [{
|
|
17837
|
+
type: Injectable,
|
|
17838
|
+
args: [{ providedIn: 'root' }]
|
|
17839
|
+
}] });
|
|
17840
|
+
|
|
17841
|
+
class AXPFileListComponent {
|
|
17842
|
+
constructor() {
|
|
17843
|
+
this.fileTypeService = inject(AXPFileTypeProviderService);
|
|
17844
|
+
this.fileStorageService = inject(AXPFileStorageService);
|
|
17845
|
+
this.commandExecutor = inject(AXPCommandExecutor);
|
|
17846
|
+
this.hooks = inject(AXPHookService);
|
|
17847
|
+
this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
17848
|
+
this.fileTypes = signal([], ...(ngDevMode ? [{ debugName: "fileTypes" }] : /* istanbul ignore next */ []));
|
|
17849
|
+
this.onRemove = output();
|
|
17850
|
+
this.onRevert = output();
|
|
17851
|
+
this.onRename = output();
|
|
17852
|
+
this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
17853
|
+
this.fileEditable = input(true, ...(ngDevMode ? [{ debugName: "fileEditable" }] : /* istanbul ignore next */ []));
|
|
17854
|
+
/** When true, edit dialog shows name, title and description fields. Default false. */
|
|
17855
|
+
this.enableTitleDescription = input(false, ...(ngDevMode ? [{ debugName: "enableTitleDescription" }] : /* istanbul ignore next */ []));
|
|
17856
|
+
this.multiple = input(true, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
17857
|
+
this.files = input([], ...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
|
|
17858
|
+
// Plugin context (passed from parent widget)
|
|
17859
|
+
this.plugins = input(undefined, ...(ngDevMode ? [{ debugName: "plugins" }] : /* istanbul ignore next */ []));
|
|
17860
|
+
this.excludePlugins = input(undefined, ...(ngDevMode ? [{ debugName: "excludePlugins" }] : /* istanbul ignore next */ []));
|
|
17861
|
+
// Capabilities API for file operations (passed from parent widget)
|
|
17862
|
+
this.capabilities = input(undefined, ...(ngDevMode ? [{ debugName: "capabilities" }] : /* istanbul ignore next */ []));
|
|
17863
|
+
/**
|
|
17864
|
+
* All files should be displayed, even those with `deleted` status.
|
|
17865
|
+
* The template will handle the visual differences based on the status.
|
|
17866
|
+
*/
|
|
17867
|
+
this.displayFiles = computed(() => this.files(), ...(ngDevMode ? [{ debugName: "displayFiles" }] : /* istanbul ignore next */ []));
|
|
17868
|
+
// Cache for per-file actions provided by hooks
|
|
17869
|
+
this.fileIdToActions = signal({}, ...(ngDevMode ? [{ debugName: "fileIdToActions" }] : /* istanbul ignore next */ []));
|
|
17870
|
+
// Global actions from FileUploaderWebhookKeys.Actions hook
|
|
17871
|
+
this.globalActions = signal([], ...(ngDevMode ? [{ debugName: "globalActions" }] : /* istanbul ignore next */ []));
|
|
17872
|
+
// Clear cache when files change to reload actions
|
|
17873
|
+
this.filesChangeEffect = effect(() => {
|
|
17874
|
+
// Track files changes to clear cache
|
|
17875
|
+
this.files();
|
|
17876
|
+
this.fileIdToActions.set({});
|
|
17877
|
+
}, ...(ngDevMode ? [{ debugName: "filesChangeEffect" }] : /* istanbul ignore next */ []));
|
|
17878
|
+
}
|
|
17879
|
+
// Default actions that can be removed via hooks
|
|
17880
|
+
getDefaultActions(file) {
|
|
17881
|
+
const defaultActions = [];
|
|
17882
|
+
// Download action (always available, including in readonly mode)
|
|
17883
|
+
defaultActions.push({
|
|
17884
|
+
id: 'attachments.default.download',
|
|
17885
|
+
plugin: 'attachments',
|
|
17886
|
+
global: true,
|
|
17887
|
+
textKey: '@general:actions.download.title',
|
|
17888
|
+
icon: 'fa-light fa-download',
|
|
17889
|
+
run: async (ctx) => {
|
|
17890
|
+
await this.handleFileDownload({ nativeEvent: { preventDefault: () => { }, stopPropagation: () => { } } }, file);
|
|
17891
|
+
},
|
|
17892
|
+
});
|
|
17893
|
+
// Edit and Remove actions (if editable and not widget/item readonly)
|
|
17894
|
+
if (!this.isItemInteractionLocked(file) &&
|
|
17895
|
+
(this.fileEditable() || (!this.fileEditable() && file.status === 'attached'))) {
|
|
17896
|
+
defaultActions.push({
|
|
17897
|
+
id: 'attachments.default.edit',
|
|
17898
|
+
plugin: 'attachments',
|
|
17899
|
+
global: true,
|
|
17900
|
+
textKey: '@general:actions.edit.title',
|
|
17901
|
+
icon: 'fa-light fa-pencil',
|
|
17902
|
+
run: async (ctx) => {
|
|
17903
|
+
await this.handleFileEdit({ stopPropagation: () => { }, preventDefault: () => { } }, file);
|
|
17904
|
+
},
|
|
17905
|
+
});
|
|
17906
|
+
defaultActions.push({
|
|
17907
|
+
id: 'attachments.default.remove',
|
|
17908
|
+
plugin: 'attachments',
|
|
17909
|
+
global: true,
|
|
17910
|
+
textKey: '@general:actions.delete.title',
|
|
17911
|
+
icon: 'fa-light fa-trash-can',
|
|
17912
|
+
run: async (ctx) => {
|
|
17913
|
+
await this.handleFileRemove({ nativeEvent: { preventDefault: () => { }, stopPropagation: () => { } } }, file);
|
|
17914
|
+
},
|
|
17915
|
+
});
|
|
17916
|
+
}
|
|
17917
|
+
return defaultActions;
|
|
17918
|
+
}
|
|
17919
|
+
/** True when the widget is readonly or this file row is locked (`readOnly`). */
|
|
17920
|
+
isItemInteractionLocked(file) {
|
|
17921
|
+
return this.readonly() || file.readOnly === true;
|
|
17922
|
+
}
|
|
17923
|
+
actionsFor(file, index) {
|
|
17924
|
+
const key = file.id ?? String(index);
|
|
17925
|
+
const map = this.fileIdToActions();
|
|
17926
|
+
if (map[key]) {
|
|
17927
|
+
return map[key];
|
|
17928
|
+
}
|
|
17929
|
+
// Lazy-load actions via hooks
|
|
17930
|
+
this.loadActionsFor(file, index).catch(console.error);
|
|
17931
|
+
return [];
|
|
17932
|
+
}
|
|
17933
|
+
async loadActionsFor(file, index) {
|
|
17934
|
+
const key = file.id ?? String(index);
|
|
17935
|
+
// Start with default actions
|
|
17936
|
+
const defaultActions = this.getDefaultActions(file);
|
|
17937
|
+
// Get per-file actions from FileUploaderWebhookKeys.FileItem.Actions hook
|
|
17938
|
+
// Note: globalActions are only for toolbar, not for per-file actions
|
|
17939
|
+
const fileItemPayload = await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.Actions, {
|
|
17940
|
+
item: file,
|
|
17941
|
+
index,
|
|
17942
|
+
itemReadOnly: file.readOnly === true,
|
|
17943
|
+
itemInteractionLocked: this.isItemInteractionLocked(file),
|
|
17944
|
+
plugins: this.plugins() ?? [],
|
|
17945
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
17946
|
+
capabilities: this.capabilities(),
|
|
17947
|
+
actions: [],
|
|
17948
|
+
});
|
|
17949
|
+
// Combine all actions: default + file-item specific (exclude globalActions from per-file items)
|
|
17950
|
+
const allActions = [...defaultActions, ...(fileItemPayload?.actions ?? [])];
|
|
17951
|
+
// Filter actions based on plugins and excludePlugins
|
|
17952
|
+
const enabledPlugins = this.plugins()?.map((p) => p.name) ?? [];
|
|
17953
|
+
const excludedPlugins = this.excludePlugins() ?? [];
|
|
17954
|
+
const filteredActions = allActions.filter((action) => {
|
|
17955
|
+
// If action has an id and is in excluded list, remove it
|
|
17956
|
+
if (action.id && excludedPlugins.some((excluded) => action.id?.includes(excluded))) {
|
|
17957
|
+
return false;
|
|
17958
|
+
}
|
|
17959
|
+
// If plugin is excluded, remove it
|
|
17960
|
+
if (excludedPlugins.includes(action.plugin)) {
|
|
17961
|
+
return false;
|
|
17962
|
+
}
|
|
17963
|
+
// If no enabled plugins, only show global actions
|
|
17964
|
+
if (!enabledPlugins || enabledPlugins.length === 0) {
|
|
17965
|
+
return action.global === true;
|
|
17966
|
+
}
|
|
17967
|
+
// Show if plugin is enabled or action is global
|
|
17968
|
+
return enabledPlugins.includes(action.plugin) || action.global === true;
|
|
17969
|
+
});
|
|
17970
|
+
const next = { ...this.fileIdToActions() };
|
|
17971
|
+
next[key] = filteredActions;
|
|
17972
|
+
this.fileIdToActions.set(next);
|
|
17973
|
+
}
|
|
17974
|
+
async ngOnInit() {
|
|
17975
|
+
this.fileTypes.set(await this.fileTypeService.items());
|
|
17976
|
+
await this.loadGlobalActions();
|
|
17977
|
+
this.isLoading.set(false);
|
|
17978
|
+
}
|
|
17979
|
+
async loadGlobalActions() {
|
|
17980
|
+
try {
|
|
17981
|
+
const payload = await this.hooks.runAsync(FileUploaderWebhookKeys.Actions, {
|
|
17982
|
+
plugins: this.plugins() ?? [],
|
|
17983
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
17984
|
+
capabilities: this.capabilities(),
|
|
17985
|
+
actions: [],
|
|
17986
|
+
});
|
|
17987
|
+
this.globalActions.set(payload?.actions ?? []);
|
|
17988
|
+
}
|
|
17989
|
+
catch (error) {
|
|
17990
|
+
console.error('Error loading global actions:', error);
|
|
17991
|
+
this.globalActions.set([]);
|
|
17992
|
+
}
|
|
17993
|
+
}
|
|
17994
|
+
getFileInfo(fileName) {
|
|
17995
|
+
const extension = fileName.split('.').pop()?.toLowerCase() || '';
|
|
17996
|
+
const extensions = this.fileTypes().flatMap((t) => t.extensions);
|
|
17997
|
+
const fileType = extensions.find((e) => e.name === extension);
|
|
17998
|
+
return {
|
|
17999
|
+
icon: fileType?.icon || 'fa-light fa-file',
|
|
18000
|
+
type: fileType?.name || 'Unknown',
|
|
18001
|
+
};
|
|
18002
|
+
}
|
|
18003
|
+
async handleFileDownload(event, file) {
|
|
18004
|
+
event.nativeEvent.preventDefault();
|
|
18005
|
+
event.nativeEvent.stopPropagation();
|
|
18006
|
+
try {
|
|
18007
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.BeforeDownload, {
|
|
18008
|
+
file,
|
|
18009
|
+
plugins: this.plugins() ?? [],
|
|
18010
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18011
|
+
capabilities: this.capabilities(),
|
|
18012
|
+
});
|
|
18013
|
+
}
|
|
18014
|
+
catch { }
|
|
18015
|
+
if (!file.source) {
|
|
18016
|
+
console.error('File source is undefined, cannot download.', file);
|
|
18017
|
+
return;
|
|
18018
|
+
}
|
|
18019
|
+
const triggerDownload = (blob, fileName) => {
|
|
18020
|
+
const url = URL.createObjectURL(blob);
|
|
18021
|
+
const link = document.createElement('a');
|
|
18022
|
+
link.href = url;
|
|
18023
|
+
link.download = fileName;
|
|
18024
|
+
document.body.appendChild(link);
|
|
18025
|
+
link.click();
|
|
18026
|
+
document.body.removeChild(link);
|
|
18027
|
+
URL.revokeObjectURL(url);
|
|
18028
|
+
};
|
|
18029
|
+
switch (file.source.kind) {
|
|
18030
|
+
case 'blob':
|
|
18031
|
+
if (file.source.value instanceof Blob) {
|
|
18032
|
+
triggerDownload(file.source.value, file.name ?? 'download');
|
|
18033
|
+
}
|
|
18034
|
+
else {
|
|
18035
|
+
console.error('Source kind is blob, but value is not a Blob.', file);
|
|
18036
|
+
}
|
|
18037
|
+
break;
|
|
18038
|
+
case 'fileId':
|
|
18039
|
+
if (typeof file.source.value === 'string') {
|
|
18040
|
+
try {
|
|
18041
|
+
const fileInfo = await this.fileStorageService.getInfo(file.source.value);
|
|
18042
|
+
if (fileInfo && fileInfo.url) {
|
|
18043
|
+
const link = document.createElement('a');
|
|
18044
|
+
link.href = fileInfo.url;
|
|
18045
|
+
link.download = file.name ?? fileInfo.name ?? 'download';
|
|
18046
|
+
link.target = '_blank';
|
|
18047
|
+
document.body.appendChild(link);
|
|
18048
|
+
link.click();
|
|
18049
|
+
document.body.removeChild(link);
|
|
18050
|
+
}
|
|
18051
|
+
else if (fileInfo && fileInfo.binary instanceof Blob) {
|
|
18052
|
+
triggerDownload(fileInfo.binary, file.name ?? fileInfo.name ?? 'download');
|
|
18053
|
+
}
|
|
18054
|
+
else {
|
|
18055
|
+
console.error('Could not retrieve file for download from fileId:', fileInfo);
|
|
18056
|
+
}
|
|
18057
|
+
}
|
|
18058
|
+
catch (error) {
|
|
18059
|
+
console.error('Error downloading file by fileId:', file.source.value, error);
|
|
18060
|
+
}
|
|
18061
|
+
}
|
|
18062
|
+
else {
|
|
18063
|
+
console.error('Source kind is fileId, but value is not a string ID.', file);
|
|
18064
|
+
}
|
|
18065
|
+
break;
|
|
18066
|
+
case 'url':
|
|
18067
|
+
if (typeof file.source.value === 'string') {
|
|
18068
|
+
const link = document.createElement('a');
|
|
18069
|
+
link.href = file.source.value;
|
|
18070
|
+
link.download = file.name ?? 'download';
|
|
18071
|
+
link.target = '_blank';
|
|
18072
|
+
document.body.appendChild(link);
|
|
18073
|
+
link.click();
|
|
18074
|
+
document.body.removeChild(link);
|
|
18075
|
+
}
|
|
18076
|
+
else {
|
|
18077
|
+
console.error('Source kind is url, but value is not a string URL.', file);
|
|
18078
|
+
}
|
|
18079
|
+
break;
|
|
18080
|
+
case 'reference':
|
|
18081
|
+
if (file.source.value &&
|
|
18082
|
+
typeof file.source.value === 'object' &&
|
|
18083
|
+
'id' in file.source.value &&
|
|
18084
|
+
'type' in file.source.value) {
|
|
18085
|
+
try {
|
|
18086
|
+
const result = await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.DownloadReference, {
|
|
18087
|
+
file,
|
|
18088
|
+
reference: file.source.value,
|
|
18089
|
+
plugins: this.plugins() ?? [],
|
|
18090
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18091
|
+
capabilities: this.capabilities(),
|
|
18092
|
+
fileInfo: undefined,
|
|
18093
|
+
});
|
|
18094
|
+
if (result?.fileInfo) {
|
|
18095
|
+
if (result.fileInfo.url) {
|
|
18096
|
+
const link = document.createElement('a');
|
|
18097
|
+
link.href = result.fileInfo.url;
|
|
18098
|
+
link.download = file.name ?? result.fileInfo.name ?? 'download';
|
|
18099
|
+
link.target = '_blank';
|
|
18100
|
+
document.body.appendChild(link);
|
|
18101
|
+
link.click();
|
|
18102
|
+
document.body.removeChild(link);
|
|
18103
|
+
}
|
|
18104
|
+
else if (result.fileInfo.binary instanceof Blob) {
|
|
18105
|
+
triggerDownload(result.fileInfo.binary, file.name ?? result.fileInfo.name ?? 'download');
|
|
18106
|
+
}
|
|
18107
|
+
else {
|
|
18108
|
+
console.error('Could not retrieve file for download from reference:', result.fileInfo);
|
|
18109
|
+
}
|
|
18110
|
+
}
|
|
18111
|
+
else {
|
|
18112
|
+
console.error('Hook did not return fileInfo for reference download.', file);
|
|
18113
|
+
}
|
|
18114
|
+
}
|
|
18115
|
+
catch (error) {
|
|
18116
|
+
console.error('Error downloading file by reference:', file.source.value, error);
|
|
18117
|
+
}
|
|
18118
|
+
}
|
|
18119
|
+
else {
|
|
18120
|
+
console.error('Source kind is reference, but value is not a valid AXPEntityReference.', file);
|
|
18121
|
+
}
|
|
18122
|
+
break;
|
|
18123
|
+
case 'preview':
|
|
18124
|
+
case 'none':
|
|
18125
|
+
default:
|
|
18126
|
+
console.error(`Download not supported for source kind: ${file.source.kind}`, file);
|
|
18127
|
+
break;
|
|
18128
|
+
}
|
|
18129
|
+
try {
|
|
18130
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterDownload, {
|
|
18131
|
+
file,
|
|
18132
|
+
plugins: this.plugins() ?? [],
|
|
18133
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18134
|
+
capabilities: this.capabilities(),
|
|
18135
|
+
});
|
|
18136
|
+
}
|
|
18137
|
+
catch { }
|
|
18138
|
+
}
|
|
18139
|
+
async handleFileRemove(event, file) {
|
|
18140
|
+
event.nativeEvent.preventDefault();
|
|
18141
|
+
event.nativeEvent.stopPropagation();
|
|
18142
|
+
this.onRemove.emit(file);
|
|
18143
|
+
try {
|
|
18144
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterRemove, {
|
|
18145
|
+
file,
|
|
18146
|
+
plugins: this.plugins() ?? [],
|
|
18147
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18148
|
+
capabilities: this.capabilities(),
|
|
18149
|
+
});
|
|
18150
|
+
}
|
|
18151
|
+
catch { }
|
|
18152
|
+
}
|
|
18153
|
+
/**
|
|
18154
|
+
* Handle revert action – emit the file so parent components can update the status.
|
|
18155
|
+
*/
|
|
18156
|
+
handleFileRevert(event, file) {
|
|
18157
|
+
if (this.isItemInteractionLocked(file)) {
|
|
18158
|
+
return;
|
|
18159
|
+
}
|
|
18160
|
+
event.nativeEvent.preventDefault();
|
|
18161
|
+
event.nativeEvent.stopPropagation();
|
|
18162
|
+
this.onRevert.emit(file);
|
|
18163
|
+
}
|
|
18164
|
+
/**
|
|
18165
|
+
* Handle file edit action – open rename popup
|
|
18166
|
+
*/
|
|
18167
|
+
async handleFileEdit(event, file) {
|
|
18168
|
+
// if (event instanceof MouseEvent) {
|
|
18169
|
+
event.stopPropagation();
|
|
18170
|
+
event.preventDefault();
|
|
18171
|
+
// } else {
|
|
18172
|
+
// event.nativeEvent.preventDefault();
|
|
18173
|
+
// event.nativeEvent.stopPropagation();
|
|
18174
|
+
// }
|
|
18175
|
+
if (this.isItemInteractionLocked(file)) {
|
|
18176
|
+
return;
|
|
18177
|
+
}
|
|
18178
|
+
const commandResult = await this.commandExecutor.execute('FileUploader:Edit', {
|
|
18179
|
+
file,
|
|
18180
|
+
plugins: this.plugins() ?? [],
|
|
18181
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18182
|
+
enableTitleDescription: this.enableTitleDescription(),
|
|
18183
|
+
});
|
|
18184
|
+
if (!commandResult?.success || !commandResult.data) {
|
|
18185
|
+
return;
|
|
18186
|
+
}
|
|
18187
|
+
const newFile = commandResult.data;
|
|
18188
|
+
this.onRename.emit(newFile);
|
|
18189
|
+
try {
|
|
18190
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterEdit, {
|
|
18191
|
+
file: newFile ?? file,
|
|
18192
|
+
previous: file,
|
|
18193
|
+
plugins: this.plugins() ?? [],
|
|
18194
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18195
|
+
capabilities: this.capabilities(),
|
|
18196
|
+
});
|
|
18197
|
+
}
|
|
18198
|
+
catch { }
|
|
18199
|
+
}
|
|
18200
|
+
ngOnDestroy() { }
|
|
18201
|
+
async runAction(action) {
|
|
18202
|
+
try {
|
|
18203
|
+
await action.run(this.capabilities());
|
|
18204
|
+
}
|
|
18205
|
+
catch (e) {
|
|
18206
|
+
console.error(e);
|
|
18207
|
+
}
|
|
18208
|
+
}
|
|
18209
|
+
getActionColor(action) {
|
|
18210
|
+
// Map default action IDs to colors
|
|
18211
|
+
if (action.id === 'attachments.default.download') {
|
|
18212
|
+
return 'primary';
|
|
18213
|
+
}
|
|
18214
|
+
if (action.id === 'attachments.default.edit') {
|
|
18215
|
+
return 'secondary';
|
|
18216
|
+
}
|
|
18217
|
+
if (action.id === 'attachments.default.remove') {
|
|
18218
|
+
return 'danger';
|
|
18219
|
+
}
|
|
18220
|
+
// Default color for custom actions
|
|
18221
|
+
return action.color ?? 'primary';
|
|
18222
|
+
}
|
|
18223
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
18224
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", 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 }, enableTitleDescription: { classPropertyName: "enableTitleDescription", publicName: "enableTitleDescription", 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]=\"{\n '--removed': file.status === 'deleted',\n '--attached': file.status === 'attached',\n '--with-meta': enableTitleDescription(),\n '--item-readonly': file.readOnly === true,\n }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n @if (enableTitleDescription()) {\n <div class=\"__content\">\n <div class=\"__primary\">{{ file.name }}{{ file.title?.trim() ? ' - ' + file.title : '' }}</div>\n @if (file.description?.trim()) {\n <div class=\"__secondary\">{{ file.description }}</div>\n }\n </div>\n } @else {\n <div class=\"__name\">{{ file.name }}</div>\n }\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple() && !isItemInteractionLocked(file)) {\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 <!-- Default + hook actions; readonly still shows download (edit/remove omitted in component logic). -->\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 </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.--item-readonly:not(.--removed){opacity:.85}: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 .__content{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column;gap:.125rem}:host .__item .__primary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__secondary{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;text-align:start;font-size:.75rem;line-height:1rem;color:var(--ax-sys-color-text-secondary)}:host .__item.--with-meta .__content{justify-content:center}:host .__item .__actions{margin-left:auto;display:flex;flex-shrink:0}@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:
|
|
18225
|
+
// Angular
|
|
18226
|
+
CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type:
|
|
18227
|
+
// ACoreX
|
|
18228
|
+
AXFormModule }, { kind: "ngmodule", type: AXTextBoxModule }, { 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: AXLabelModule }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type:
|
|
18229
|
+
// Platform
|
|
18230
|
+
AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }] }); }
|
|
18231
|
+
}
|
|
18232
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileListComponent, decorators: [{
|
|
18233
|
+
type: Component,
|
|
18234
|
+
args: [{ selector: 'axp-file-list', imports: [
|
|
18235
|
+
// Angular
|
|
18236
|
+
CommonModule,
|
|
18237
|
+
// ACoreX
|
|
18238
|
+
AXFormModule,
|
|
18239
|
+
AXTextBoxModule,
|
|
18240
|
+
AXButtonModule,
|
|
18241
|
+
AXLabelModule,
|
|
18242
|
+
AXCheckBoxModule,
|
|
18243
|
+
AXDecoratorModule,
|
|
18244
|
+
AXTranslationModule,
|
|
18245
|
+
// Platform
|
|
18246
|
+
AXPStateMessageComponent,
|
|
18247
|
+
], providers: [], template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{\n '--removed': file.status === 'deleted',\n '--attached': file.status === 'attached',\n '--with-meta': enableTitleDescription(),\n '--item-readonly': file.readOnly === true,\n }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n @if (enableTitleDescription()) {\n <div class=\"__content\">\n <div class=\"__primary\">{{ file.name }}{{ file.title?.trim() ? ' - ' + file.title : '' }}</div>\n @if (file.description?.trim()) {\n <div class=\"__secondary\">{{ file.description }}</div>\n }\n </div>\n } @else {\n <div class=\"__name\">{{ file.name }}</div>\n }\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple() && !isItemInteractionLocked(file)) {\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 <!-- Default + hook actions; readonly still shows download (edit/remove omitted in component logic). -->\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 </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.--item-readonly:not(.--removed){opacity:.85}: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 .__content{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column;gap:.125rem}:host .__item .__primary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__secondary{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;text-align:start;font-size:.75rem;line-height:1rem;color:var(--ax-sys-color-text-secondary)}:host .__item.--with-meta .__content{justify-content:center}:host .__item .__actions{margin-left:auto;display:flex;flex-shrink:0}@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"] }]
|
|
18248
|
+
}], 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 }] }], enableTitleDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableTitleDescription", 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 }] }] } });
|
|
18249
|
+
|
|
18250
|
+
//#region ---- Attachment fingerprint (no blob comparison) ----
|
|
18251
|
+
/** Stable fingerprint for one file row; ignores Blob binary content. */
|
|
18252
|
+
function fingerprintAttachmentItem(item) {
|
|
18253
|
+
if (!item || typeof item !== 'object') {
|
|
18254
|
+
return '';
|
|
18255
|
+
}
|
|
18256
|
+
const file = item;
|
|
18257
|
+
const source = file['source'];
|
|
18258
|
+
const kind = source?.['kind'];
|
|
18259
|
+
let sourceToken = '';
|
|
18260
|
+
if (kind === 'blob') {
|
|
18261
|
+
const blob = source?.['value'];
|
|
18262
|
+
const size = blob instanceof Blob ? blob.size : file['size'];
|
|
18263
|
+
sourceToken = `blob:${size ?? 0}`;
|
|
18264
|
+
}
|
|
18265
|
+
else if (kind === 'fileId') {
|
|
18266
|
+
sourceToken = `fileId:${String(source?.['value'] ?? '')}`;
|
|
18267
|
+
}
|
|
18268
|
+
else {
|
|
18269
|
+
sourceToken = `${String(kind ?? '')}:${String(source?.['value'] ?? '')}`;
|
|
18270
|
+
}
|
|
18271
|
+
return [
|
|
18272
|
+
String(file['id'] ?? ''),
|
|
18273
|
+
String(file['name'] ?? ''),
|
|
18274
|
+
String(file['status'] ?? ''),
|
|
18275
|
+
String(file['size'] ?? ''),
|
|
18276
|
+
sourceToken,
|
|
18277
|
+
String(file['title'] ?? ''),
|
|
18278
|
+
String(file['description'] ?? ''),
|
|
18279
|
+
].join('|');
|
|
18280
|
+
}
|
|
18281
|
+
/** Order-independent fingerprint for attachment lists. */
|
|
18282
|
+
function fingerprintAttachments(value) {
|
|
18283
|
+
if (!isArray(value) || value.length === 0) {
|
|
18284
|
+
return '';
|
|
18285
|
+
}
|
|
18286
|
+
return value.map(fingerprintAttachmentItem).sort().join(';');
|
|
18287
|
+
}
|
|
18288
|
+
/** Identity key for semantic compare (ignores blob/source shape differences on load, includes status). */
|
|
18289
|
+
function attachmentIdentityKey(item) {
|
|
18290
|
+
if (!item || typeof item !== 'object') {
|
|
18291
|
+
return '';
|
|
18292
|
+
}
|
|
18293
|
+
const file = item;
|
|
18294
|
+
const source = file['source'];
|
|
18295
|
+
const persistedId = source?.['kind'] === 'fileId' ? String(source['value'] ?? '') : String(file['id'] ?? '');
|
|
18296
|
+
return [persistedId, String(file['name'] ?? ''), String(file['status'] ?? '')].join('|');
|
|
18297
|
+
}
|
|
18298
|
+
/** True when lists represent the same attachments regardless of blob vs fileId shape. */
|
|
18299
|
+
function attachmentsSemanticallyEqual(a, b) {
|
|
18300
|
+
const listA = isArray(a) ? a : [];
|
|
18301
|
+
const listB = isArray(b) ? b : [];
|
|
18302
|
+
if (listA.length !== listB.length) {
|
|
18303
|
+
return false;
|
|
18304
|
+
}
|
|
18305
|
+
const keysA = listA.map(attachmentIdentityKey).sort();
|
|
18306
|
+
const keysB = listB.map(attachmentIdentityKey).sort();
|
|
18307
|
+
return keysA.every((key, index) => key === keysB[index]);
|
|
18308
|
+
}
|
|
18309
|
+
/** Attachment rows that remain after a successful save (drops soft-deleted rows). */
|
|
18310
|
+
function committedAttachments(value) {
|
|
18311
|
+
if (!isArray(value)) {
|
|
18312
|
+
return [];
|
|
18313
|
+
}
|
|
18314
|
+
return value.filter((item) => {
|
|
18315
|
+
if (!item || typeof item !== 'object') {
|
|
18316
|
+
return false;
|
|
18317
|
+
}
|
|
18318
|
+
return item.status !== 'deleted';
|
|
18319
|
+
});
|
|
18320
|
+
}
|
|
18321
|
+
/**
|
|
18322
|
+
* Post-save attachment list: drops soft-deleted rows and promotes pending `attached` rows to `uploaded`
|
|
18323
|
+
* so deferred-save pages get the same delete/revert behavior as after a full reload.
|
|
18324
|
+
*/
|
|
18325
|
+
function persistedAttachments(value) {
|
|
18326
|
+
return committedAttachments(value).map((item) => item.status === 'attached' ? { ...item, status: 'uploaded' } : item);
|
|
18327
|
+
}
|
|
18328
|
+
//#endregion
|
|
18329
|
+
|
|
18330
|
+
//#endregion
|
|
18331
|
+
//#region ---- Helpers ----
|
|
18332
|
+
function isFileListItem(value) {
|
|
18333
|
+
return value != null && typeof value === 'object' && typeof get(value, 'name') === 'string';
|
|
18334
|
+
}
|
|
18335
|
+
/** Whether a list-row value represents one attachment (file item, id reference, or fileId source). */
|
|
18336
|
+
function isAttachmentListEntry(value) {
|
|
18337
|
+
if (isFileListItem(value)) {
|
|
18338
|
+
return true;
|
|
18339
|
+
}
|
|
18340
|
+
if (isString(value) && value.trim().length > 0) {
|
|
18341
|
+
return true;
|
|
18342
|
+
}
|
|
18343
|
+
if (value != null && typeof value === 'object') {
|
|
18344
|
+
const id = get(value, 'id');
|
|
18345
|
+
if (typeof id === 'string' && id.trim().length > 0) {
|
|
18346
|
+
return true;
|
|
18347
|
+
}
|
|
18348
|
+
const fileId = get(value, 'source.value');
|
|
18349
|
+
if (get(value, 'source.kind') === 'fileId' && typeof fileId === 'string' && fileId.trim().length > 0) {
|
|
18350
|
+
return true;
|
|
18351
|
+
}
|
|
18352
|
+
const entryName = get(value, 'name');
|
|
18353
|
+
if (typeof entryName === 'string' && entryName.trim().length > 0) {
|
|
18354
|
+
return true;
|
|
18355
|
+
}
|
|
18356
|
+
}
|
|
18357
|
+
return false;
|
|
18358
|
+
}
|
|
18359
|
+
function parseAttachmentFieldEntries(fieldValue) {
|
|
18360
|
+
if (fieldValue == null) {
|
|
18361
|
+
return [];
|
|
18362
|
+
}
|
|
18363
|
+
if (isString(fieldValue)) {
|
|
18364
|
+
const trimmed = fieldValue.trim();
|
|
18365
|
+
if (!trimmed) {
|
|
18366
|
+
return [];
|
|
18367
|
+
}
|
|
18368
|
+
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
18369
|
+
try {
|
|
18370
|
+
const parsed = JSON.parse(trimmed);
|
|
18371
|
+
return castArray(parsed);
|
|
18372
|
+
}
|
|
18373
|
+
catch {
|
|
18374
|
+
return [trimmed];
|
|
18375
|
+
}
|
|
18376
|
+
}
|
|
18377
|
+
return [trimmed];
|
|
18378
|
+
}
|
|
18379
|
+
return castArray(fieldValue);
|
|
18380
|
+
}
|
|
18381
|
+
function attachmentFieldCount(fieldValue) {
|
|
18382
|
+
return parseAttachmentFieldEntries(fieldValue).filter(isAttachmentListEntry).length;
|
|
18383
|
+
}
|
|
18384
|
+
function normalizeEntityFieldToFileList(fieldValue) {
|
|
18385
|
+
return parseAttachmentFieldEntries(fieldValue).filter(isFileListItem);
|
|
18386
|
+
}
|
|
18387
|
+
/** Resolve entity scope for column popup / load when widget options carry list-row expressions. */
|
|
18388
|
+
function resolveEntityId(entityIdOption, rowData) {
|
|
18389
|
+
if (isString(entityIdOption) && entityIdOption.trim() && !entityIdOption.includes('{{')) {
|
|
18390
|
+
return entityIdOption.trim();
|
|
18391
|
+
}
|
|
18392
|
+
const rowId = get(rowData, 'id') ?? get(rowData, 'Id');
|
|
18393
|
+
if (rowId != null && String(rowId).trim()) {
|
|
18394
|
+
return String(rowId).trim();
|
|
18395
|
+
}
|
|
18396
|
+
return undefined;
|
|
18397
|
+
}
|
|
18398
|
+
function resolveFileUploaderEntityScope(options, rowData, fieldFallback) {
|
|
18399
|
+
const entityBlock = get(options, 'entity');
|
|
18400
|
+
if (entityBlock != null && typeof entityBlock === 'object') {
|
|
18401
|
+
const name = get(entityBlock, 'name');
|
|
18402
|
+
const field = get(entityBlock, 'field') ?? fieldFallback;
|
|
18403
|
+
const entityId = resolveEntityId(get(entityBlock, 'id'), rowData);
|
|
18404
|
+
if (typeof name === 'string' &&
|
|
18405
|
+
name.trim() &&
|
|
18406
|
+
entityId &&
|
|
18407
|
+
typeof field === 'string' &&
|
|
18408
|
+
field.trim()) {
|
|
18409
|
+
return { name: name.trim(), id: entityId, field: field.trim() };
|
|
18410
|
+
}
|
|
18411
|
+
}
|
|
18412
|
+
const entityName = get(options, 'entityName') ?? get(options, 'entityRef');
|
|
18413
|
+
const field = get(options, 'entityField') ?? fieldFallback;
|
|
18414
|
+
const entityId = resolveEntityId(get(options, 'entityId'), rowData);
|
|
18415
|
+
if (typeof entityName !== 'string' ||
|
|
18416
|
+
!entityName.trim() ||
|
|
18417
|
+
!entityId ||
|
|
18418
|
+
typeof field !== 'string' ||
|
|
18419
|
+
!field.trim()) {
|
|
18420
|
+
return undefined;
|
|
18421
|
+
}
|
|
18422
|
+
return { name: entityName.trim(), id: entityId, field: field.trim() };
|
|
18423
|
+
}
|
|
18424
|
+
//#endregion
|
|
18425
|
+
|
|
18426
|
+
class AXPFileUploaderLoadFilesQuery {
|
|
18427
|
+
constructor() {
|
|
18428
|
+
//#region ---- Services & Dependencies ----
|
|
18429
|
+
this.injector = inject(Injector);
|
|
18430
|
+
}
|
|
18431
|
+
//#endregion
|
|
18432
|
+
//#region ---- Query Execution ----
|
|
18433
|
+
async fetch(input) {
|
|
18434
|
+
const { name: entityRef, id: entityId, field } = input;
|
|
18435
|
+
if (!entityRef?.trim() || !entityId?.trim() || !field?.trim()) {
|
|
18436
|
+
throw new Error('entityRef, entityId, and field are required.');
|
|
18437
|
+
}
|
|
18438
|
+
const record = await runInInjectionContext(this.injector, () => {
|
|
18439
|
+
const dataService = new AXMEntityCrudServiceImpl(entityRef);
|
|
18440
|
+
return dataService.getOne(entityId);
|
|
18441
|
+
});
|
|
18442
|
+
const fieldValue = get(record, field);
|
|
18443
|
+
const files = normalizeEntityFieldToFileList(fieldValue);
|
|
18444
|
+
const count = attachmentFieldCount(fieldValue);
|
|
18445
|
+
return { files, count };
|
|
18446
|
+
}
|
|
18447
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderLoadFilesQuery, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
18448
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderLoadFilesQuery, providedIn: 'root' }); }
|
|
18449
|
+
}
|
|
18450
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderLoadFilesQuery, decorators: [{
|
|
18451
|
+
type: Injectable,
|
|
18452
|
+
args: [{ providedIn: 'root' }]
|
|
18453
|
+
}] });
|
|
18454
|
+
|
|
18455
|
+
class AXPFileUploaderSaveFilesCommand {
|
|
18456
|
+
constructor() {
|
|
18457
|
+
//#region ---- Services & Dependencies ----
|
|
18458
|
+
this.injector = inject(Injector);
|
|
18459
|
+
//#endregion
|
|
18460
|
+
}
|
|
18461
|
+
//#endregion
|
|
18462
|
+
//#region ---- Command Execution ----
|
|
18463
|
+
async execute(input) {
|
|
18464
|
+
const { name: entityRef, id: entityId, field } = input;
|
|
18465
|
+
const files = (input.files ?? []).filter(isFileListItem);
|
|
18466
|
+
if (!entityRef?.trim() || !entityId?.trim() || !field?.trim()) {
|
|
18467
|
+
return {
|
|
18468
|
+
success: false,
|
|
18469
|
+
message: { text: 'entityRef, entityId, and field are required.' },
|
|
18470
|
+
};
|
|
18471
|
+
}
|
|
18472
|
+
try {
|
|
18473
|
+
const entityData = await runInInjectionContext(this.injector, async () => {
|
|
18474
|
+
const dataService = new AXMEntityCrudServiceImpl(entityRef);
|
|
18475
|
+
return dataService.updateOne(entityId, { [field]: files });
|
|
18476
|
+
});
|
|
18477
|
+
return {
|
|
18478
|
+
success: true,
|
|
18479
|
+
data: { entityData: { ...entityData, [field]: files } },
|
|
18480
|
+
};
|
|
18481
|
+
}
|
|
18482
|
+
catch (error) {
|
|
18483
|
+
return {
|
|
18484
|
+
success: false,
|
|
18485
|
+
message: {
|
|
18486
|
+
text: error instanceof Error ? error.message : 'Failed to save files.',
|
|
18487
|
+
},
|
|
18488
|
+
};
|
|
18489
|
+
}
|
|
18490
|
+
}
|
|
18491
|
+
}
|
|
18492
|
+
|
|
18493
|
+
//#region ---- File uploader widget API types ----
|
|
18494
|
+
function resolveFileUploaderEditDialog(options) {
|
|
18495
|
+
return options?.['editDialog'];
|
|
18496
|
+
}
|
|
18497
|
+
function isFileUploaderEditDialogAuto(editDialog) {
|
|
18498
|
+
return editDialog?.mode === 'auto';
|
|
18499
|
+
}
|
|
18500
|
+
function hasFileUploaderTitleOrDescriptionFields(editDialog) {
|
|
18501
|
+
return !!(editDialog?.fields?.title || editDialog?.fields?.description);
|
|
18502
|
+
}
|
|
18503
|
+
//#endregion
|
|
18504
|
+
|
|
18505
|
+
class AXPFileUploaderWidgetService {
|
|
18506
|
+
constructor() {
|
|
18507
|
+
//#region ---- Services & Dependencies ----
|
|
18508
|
+
this.popupService = inject(AXPopupService);
|
|
18509
|
+
this.translate = inject(AXTranslationService);
|
|
18510
|
+
this.commandExecutor = inject(AXPCommandExecutor);
|
|
18511
|
+
this.queryExecutor = inject(AXPQueryExecutor);
|
|
18512
|
+
}
|
|
18513
|
+
//#endregion
|
|
18514
|
+
//#region ---- Public API ----
|
|
18515
|
+
async showFileList(options) {
|
|
18516
|
+
const entity = options?.entity;
|
|
18517
|
+
let files = options?.files;
|
|
18518
|
+
if (entity) {
|
|
18519
|
+
const filesResult = await this.queryExecutor.fetch('FileUploader:LoadFiles', entity);
|
|
18520
|
+
if (!filesResult) {
|
|
18521
|
+
return undefined;
|
|
18522
|
+
}
|
|
18523
|
+
files = filesResult.files ?? [];
|
|
18524
|
+
}
|
|
18525
|
+
const resolved = {
|
|
18526
|
+
files: files ?? [],
|
|
18527
|
+
readonly: options?.readonly ?? false,
|
|
18528
|
+
multiple: options?.multiple ?? false,
|
|
18529
|
+
accept: options?.accept ?? '*',
|
|
18530
|
+
fileEditable: options?.fileEditable ?? true,
|
|
18531
|
+
maxFileSize: options?.maxFileSize ?? 1024 * 1024 * 10,
|
|
18532
|
+
showAddItemButton: options?.showAddItemButton ?? true,
|
|
18533
|
+
plugins: options?.plugins ?? [],
|
|
18534
|
+
editDialog: options?.editDialog,
|
|
18535
|
+
};
|
|
18536
|
+
const component = await import('./acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs').then((m) => m.AXPFileListPopupComponent);
|
|
18537
|
+
const result = await this.popupService.open(component, {
|
|
18538
|
+
title: await this.translate.translateAsync('@document-management:terms.common.file'),
|
|
18539
|
+
data: {
|
|
18540
|
+
files: signal(resolved.files),
|
|
18541
|
+
isReadonly: signal(resolved.readonly),
|
|
18542
|
+
multiple: signal(resolved.multiple),
|
|
18543
|
+
accept: signal(resolved.accept),
|
|
18544
|
+
maxFileSize: signal(resolved.maxFileSize),
|
|
18545
|
+
fileEditable: signal(resolved.fileEditable),
|
|
18546
|
+
showAddItemButton: signal(resolved.showAddItemButton),
|
|
18547
|
+
plugins: signal(resolved.plugins),
|
|
18548
|
+
editDialog: signal(resolved.editDialog),
|
|
18549
|
+
},
|
|
18550
|
+
});
|
|
18551
|
+
const updatedFiles = result?.data?.data?.files;
|
|
18552
|
+
if (!updatedFiles) {
|
|
18553
|
+
return undefined;
|
|
18554
|
+
}
|
|
18555
|
+
if (!entity) {
|
|
18556
|
+
return updatedFiles;
|
|
18557
|
+
}
|
|
18558
|
+
const saveResult = await this.commandExecutor.execute('FileUploader:SaveFiles', {
|
|
18559
|
+
...entity,
|
|
18560
|
+
files: updatedFiles,
|
|
18561
|
+
});
|
|
18562
|
+
if (!saveResult?.success) {
|
|
18563
|
+
return undefined;
|
|
18564
|
+
}
|
|
18565
|
+
return updatedFiles;
|
|
18566
|
+
}
|
|
18567
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
18568
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, providedIn: 'root' }); }
|
|
18569
|
+
}
|
|
18570
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, decorators: [{
|
|
18571
|
+
type: Injectable,
|
|
18572
|
+
args: [{
|
|
18573
|
+
providedIn: 'root',
|
|
18574
|
+
}]
|
|
18575
|
+
}] });
|
|
18576
|
+
|
|
18577
|
+
class AXPFileUploaderWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
18578
|
+
//#endregion
|
|
18579
|
+
//#region ---- Lifecycle ----
|
|
18580
|
+
constructor() {
|
|
18581
|
+
super();
|
|
18582
|
+
//#region ---- State ----
|
|
18583
|
+
this.fileCount = signal(0, ...(ngDevMode ? [{ debugName: "fileCount" }] : /* istanbul ignore next */ []));
|
|
18584
|
+
this.loadRequestId = 0;
|
|
18585
|
+
//#endregion
|
|
18586
|
+
//#region ---- Services & Dependencies ----
|
|
18587
|
+
this.fileService = inject(AXPFileUploaderWidgetService);
|
|
18588
|
+
this.queryExecutor = inject(AXPQueryExecutor);
|
|
18589
|
+
effect(() => {
|
|
18590
|
+
const raw = this.rawValue;
|
|
18591
|
+
const row = this.rowData;
|
|
18592
|
+
const opts = this.options ?? {};
|
|
18593
|
+
const path = this.path;
|
|
18594
|
+
const inlineCount = attachmentFieldCount(raw);
|
|
18595
|
+
if (inlineCount > 0) {
|
|
18596
|
+
untracked(() => this.fileCount.set(inlineCount));
|
|
18597
|
+
return;
|
|
18598
|
+
}
|
|
18599
|
+
const entity = resolveFileUploaderEntityScope(opts, row, path);
|
|
18600
|
+
if (!entity) {
|
|
18601
|
+
untracked(() => this.fileCount.set(0));
|
|
18602
|
+
return;
|
|
18603
|
+
}
|
|
18604
|
+
const requestId = ++this.loadRequestId;
|
|
18605
|
+
untracked(() => {
|
|
18606
|
+
console.log('loadFiles for count', entity);
|
|
18607
|
+
void this.queryExecutor
|
|
18608
|
+
.fetch('FileUploader:LoadFiles', entity)
|
|
18609
|
+
.then((result) => {
|
|
18610
|
+
if (requestId !== this.loadRequestId) {
|
|
18611
|
+
return;
|
|
18612
|
+
}
|
|
18613
|
+
const count = result?.count ?? result?.files?.length ?? 0;
|
|
18614
|
+
this.fileCount.set(count);
|
|
18615
|
+
})
|
|
18616
|
+
.catch(() => {
|
|
18617
|
+
if (requestId === this.loadRequestId) {
|
|
18618
|
+
this.fileCount.set(0);
|
|
18619
|
+
}
|
|
18620
|
+
});
|
|
18621
|
+
});
|
|
18622
|
+
});
|
|
18623
|
+
}
|
|
18624
|
+
//#endregion
|
|
18625
|
+
//#region ---- UI Handlers ----
|
|
18626
|
+
async openFileList() {
|
|
18627
|
+
const entity = resolveFileUploaderEntityScope(this.options ?? {}, this.rowData, this.path);
|
|
18628
|
+
await this.fileService.showFileList({
|
|
18629
|
+
entity,
|
|
18630
|
+
readonly: true,
|
|
18631
|
+
});
|
|
18632
|
+
}
|
|
18633
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
18634
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPFileUploaderWidgetColumnComponent, isStandalone: true, selector: "axp-file-uploader-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, usesInheritance: true, ngImport: i0, template: `
|
|
18635
|
+
@if (fileCount() > 0) {
|
|
18636
|
+
<span
|
|
18637
|
+
class="ax-cursor-pointer ax-text-primary ax-underline"
|
|
18638
|
+
(click)="openFileList()"
|
|
18639
|
+
>
|
|
18640
|
+
{{ fileCount() }} {{ '@document-management:file' | translate | async }}
|
|
18641
|
+
</span>
|
|
18642
|
+
} @else {
|
|
18643
|
+
<span class="ax-text-muted">---</span>
|
|
18644
|
+
}
|
|
18645
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
18646
|
+
}
|
|
18647
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetColumnComponent, decorators: [{
|
|
18648
|
+
type: Component,
|
|
18649
|
+
args: [{
|
|
18650
|
+
selector: 'axp-file-uploader-widget-column',
|
|
18651
|
+
template: `
|
|
18652
|
+
@if (fileCount() > 0) {
|
|
18653
|
+
<span
|
|
18654
|
+
class="ax-cursor-pointer ax-text-primary ax-underline"
|
|
18655
|
+
(click)="openFileList()"
|
|
18656
|
+
>
|
|
18657
|
+
{{ fileCount() }} {{ '@document-management:file' | translate | async }}
|
|
18658
|
+
</span>
|
|
18659
|
+
} @else {
|
|
18660
|
+
<span class="ax-text-muted">---</span>
|
|
18661
|
+
}
|
|
18662
|
+
`,
|
|
18663
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
18664
|
+
imports: [AXTranslationModule, AsyncPipe],
|
|
18665
|
+
inputs: ['rawValue', 'rowData'],
|
|
18666
|
+
}]
|
|
18667
|
+
}], ctorParameters: () => [] });
|
|
18668
|
+
|
|
18669
|
+
var fileUploaderWidgetColumn_component = /*#__PURE__*/Object.freeze({
|
|
18670
|
+
__proto__: null,
|
|
18671
|
+
AXPFileUploaderWidgetColumnComponent: AXPFileUploaderWidgetColumnComponent
|
|
18672
|
+
});
|
|
18673
|
+
|
|
18674
|
+
class AXPFileUploaderWidgetEditComponent extends AXPValueWidgetComponent {
|
|
18675
|
+
constructor() {
|
|
18676
|
+
super(...arguments);
|
|
18677
|
+
this.fileService = inject(AXFileService);
|
|
18678
|
+
this.hooks = inject(AXPHookService);
|
|
18679
|
+
this.fileActionsService = inject(AXPFileActionsService);
|
|
18680
|
+
this.commandExecutor = inject(AXPCommandExecutor);
|
|
18681
|
+
//#region ---- Dirty tracking (deferred save pages) ----
|
|
18682
|
+
this.dirtyChangeSubject = new Subject();
|
|
18683
|
+
this.uploadActionsChangeSubject = new Subject();
|
|
18684
|
+
this.savedSnapshot = signal(undefined, ...(ngDevMode ? [{ debugName: "savedSnapshot" }] : /* istanbul ignore next */ []));
|
|
18685
|
+
this.baselineFingerprint = signal('', ...(ngDevMode ? [{ debugName: "baselineFingerprint" }] : /* istanbul ignore next */ []));
|
|
18686
|
+
this.baselinePendingReconcile = signal(true, ...(ngDevMode ? [{ debugName: "baselinePendingReconcile" }] : /* istanbul ignore next */ []));
|
|
18687
|
+
this.isDirtySignal = signal(false, ...(ngDevMode ? [{ debugName: "isDirtySignal" }] : /* istanbul ignore next */ []));
|
|
18688
|
+
this.skipDirtyEvaluation = false;
|
|
18689
|
+
/** Status before soft-delete; used for restore when baseline was not synced (section layout). */
|
|
18690
|
+
this.statusBeforeDelete = new Map();
|
|
18691
|
+
this.autoBaselineSynced = signal(false, ...(ngDevMode ? [{ debugName: "autoBaselineSynced" }] : /* istanbul ignore next */ []));
|
|
18692
|
+
/** Sync baseline from context when not wired by a deferred-save page (section layout). */
|
|
18693
|
+
this.baselineInitEffect = effect(() => {
|
|
18694
|
+
if (this.autoBaselineSynced()) {
|
|
18695
|
+
return;
|
|
18696
|
+
}
|
|
18697
|
+
const value = this.getValue();
|
|
18698
|
+
if (value == null) {
|
|
18699
|
+
return;
|
|
18700
|
+
}
|
|
18701
|
+
this.syncBaseline(value);
|
|
18702
|
+
}, ...(ngDevMode ? [{ debugName: "baselineInitEffect" }] : /* istanbul ignore next */ []));
|
|
18703
|
+
//#endregion
|
|
18704
|
+
this.multiple = computed(() => this.options()['multiple'], ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
18705
|
+
this.acceptOverride = signal(undefined, ...(ngDevMode ? [{ debugName: "acceptOverride" }] : /* istanbul ignore next */ []));
|
|
18706
|
+
this.accept = computed(() => this.acceptOverride() ?? this.options()['accept'], ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
|
|
18707
|
+
this.plugins = computed(() => this.options()['plugins'] ?? [], ...(ngDevMode ? [{ debugName: "plugins" }] : /* istanbul ignore next */ []));
|
|
18708
|
+
this.pluginNames = computed(() => (this.plugins() ?? []).map((p) => p.name), ...(ngDevMode ? [{ debugName: "pluginNames" }] : /* istanbul ignore next */ []));
|
|
18709
|
+
this.excludePlugins = computed(() => this.options()['excludePlugins'], ...(ngDevMode ? [{ debugName: "excludePlugins" }] : /* istanbul ignore next */ []));
|
|
18710
|
+
this.fileEditable = computed(() => {
|
|
18711
|
+
if (this.options()['readonly']) {
|
|
18712
|
+
return false;
|
|
18713
|
+
}
|
|
18714
|
+
return this.options()['fileEditable'];
|
|
18715
|
+
}, ...(ngDevMode ? [{ debugName: "fileEditable" }] : /* istanbul ignore next */ []));
|
|
18716
|
+
this.editDialog = computed(() => resolveFileUploaderEditDialog(this.options()), ...(ngDevMode ? [{ debugName: "editDialog" }] : /* istanbul ignore next */ []));
|
|
18717
|
+
this.enableTitleDescription = computed(() => hasFileUploaderTitleOrDescriptionFields(this.editDialog()), ...(ngDevMode ? [{ debugName: "enableTitleDescription" }] : /* istanbul ignore next */ []));
|
|
18718
|
+
/** When mode is `auto`, show the edit dialog after each file select. */
|
|
18719
|
+
this.showEditDialogAfterSelect = computed(() => isFileUploaderEditDialogAuto(this.editDialog()), ...(ngDevMode ? [{ debugName: "showEditDialogAfterSelect" }] : /* istanbul ignore next */ []));
|
|
18720
|
+
this.readonly = computed(() => Boolean(this.options()['readonly']), ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
18721
|
+
this.showBorder = computed(() => this.options()['showBorder'] ?? true, ...(ngDevMode ? [{ debugName: "showBorder" }] : /* istanbul ignore next */ []));
|
|
18722
|
+
this.showAddItemButton = computed(() => this.options()['showAddItemButton'] ?? true, ...(ngDevMode ? [{ debugName: "showAddItemButton" }] : /* istanbul ignore next */ []));
|
|
18723
|
+
this.maxFileSize = computed(() => this.options()['maxFileSize'], ...(ngDevMode ? [{ debugName: "maxFileSize" }] : /* istanbul ignore next */ []));
|
|
18724
|
+
// Drag and drop state
|
|
18725
|
+
this.isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : /* istanbul ignore next */ []));
|
|
18726
|
+
this.innerActions = signal([], ...(ngDevMode ? [{ debugName: "innerActions" }] : /* istanbul ignore next */ []));
|
|
18727
|
+
this.fileActions = computed(() => {
|
|
18728
|
+
const enabledPlugins = this.pluginNames() ?? [];
|
|
18729
|
+
const excludedPlugins = this.excludePlugins() ?? [];
|
|
18730
|
+
const all = this.innerActions() ?? [];
|
|
18731
|
+
return all.filter((a) => {
|
|
18732
|
+
if (excludedPlugins.includes(a.plugin)) {
|
|
18733
|
+
return false;
|
|
18734
|
+
}
|
|
18735
|
+
if (!enabledPlugins || enabledPlugins.length === 0) {
|
|
18736
|
+
return a.global === true;
|
|
18737
|
+
}
|
|
18738
|
+
return enabledPlugins.includes(a.plugin) || a.global === true;
|
|
18739
|
+
});
|
|
18740
|
+
}, ...(ngDevMode ? [{ debugName: "fileActions" }] : /* istanbul ignore next */ []));
|
|
18741
|
+
this.files = computed(() => (this.getValue() ?? []).map((file) => ({
|
|
18742
|
+
...file,
|
|
18743
|
+
})) ?? [], ...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
|
|
18744
|
+
this.capabilities = {
|
|
18745
|
+
getFiles: () => this.files(),
|
|
18746
|
+
setFiles: (files) => this.setValue(files),
|
|
18747
|
+
addFiles: (files) => {
|
|
18748
|
+
if (this.readonly()) {
|
|
18749
|
+
return;
|
|
18750
|
+
}
|
|
18751
|
+
if (this.showEditDialogAfterSelect()) {
|
|
18752
|
+
this.addFilesWithEditDialogAsync(files);
|
|
18753
|
+
}
|
|
18754
|
+
else {
|
|
18755
|
+
this.setValue([...(this.getValue() ?? []), ...files]);
|
|
18756
|
+
}
|
|
18757
|
+
},
|
|
18758
|
+
updateFile: (predicate, updater) => {
|
|
18759
|
+
const current = this.getValue() ?? [];
|
|
18760
|
+
const next = current.map((f) => (predicate(f) ? updater(f) : f));
|
|
18761
|
+
this.setValue(next);
|
|
18762
|
+
},
|
|
18763
|
+
removeFile: (file) => this.removeFile(file),
|
|
18764
|
+
clear: () => this.clear(),
|
|
18765
|
+
getFileById: (id) => (this.getValue() ?? []).find((f) => f.id === id),
|
|
18766
|
+
updateFileById: (id, next) => {
|
|
18767
|
+
const current = this.getValue() ?? [];
|
|
18768
|
+
const idx = current.findIndex((f) => f.id === id);
|
|
18769
|
+
if (idx === -1) {
|
|
18770
|
+
return;
|
|
18771
|
+
}
|
|
18772
|
+
const updated = { ...current[idx], ...next };
|
|
18773
|
+
const cloned = [...current];
|
|
18774
|
+
cloned[idx] = updated;
|
|
18775
|
+
this.setValue(cloned);
|
|
18776
|
+
},
|
|
18777
|
+
};
|
|
18778
|
+
}
|
|
18779
|
+
ngOnInit() {
|
|
18780
|
+
super.ngOnInit();
|
|
18781
|
+
this.configureFromHooks();
|
|
18782
|
+
this.loadActions();
|
|
18783
|
+
}
|
|
18784
|
+
setValue(value) {
|
|
18785
|
+
super.setValue(value);
|
|
18786
|
+
if (this.skipDirtyEvaluation) {
|
|
18787
|
+
return;
|
|
18788
|
+
}
|
|
18789
|
+
this.evaluateDirty();
|
|
18790
|
+
}
|
|
18791
|
+
/** Align dirty baseline with persisted attachments (call when record loads or after discard remount). */
|
|
18792
|
+
syncBaseline(saved) {
|
|
18793
|
+
this.savedSnapshot.set(cloneDeep(saved));
|
|
18794
|
+
this.baselineFingerprint.set(fingerprintAttachments(saved));
|
|
18795
|
+
this.baselinePendingReconcile.set(true);
|
|
18796
|
+
this.statusBeforeDelete.clear();
|
|
18797
|
+
this.autoBaselineSynced.set(true);
|
|
18798
|
+
this.evaluateDirty();
|
|
18799
|
+
}
|
|
18800
|
+
/** Mark current list as saved; clears dirty and drops soft-deleted rows from the UI. */
|
|
18801
|
+
markSaved() {
|
|
18802
|
+
const attachments = persistedAttachments(this.getValue() ?? []);
|
|
18803
|
+
this.skipDirtyEvaluation = true;
|
|
18804
|
+
this.setValue(cloneDeep(attachments));
|
|
18805
|
+
this.skipDirtyEvaluation = false;
|
|
18806
|
+
this.savedSnapshot.set(cloneDeep(attachments));
|
|
18807
|
+
this.baselineFingerprint.set(fingerprintAttachments(attachments));
|
|
18808
|
+
this.baselinePendingReconcile.set(false);
|
|
18809
|
+
this.statusBeforeDelete.clear();
|
|
18810
|
+
this.setDirty(false);
|
|
18811
|
+
}
|
|
18812
|
+
/** Revert widget value to last saved baseline. */
|
|
18813
|
+
discardToBaseline() {
|
|
18814
|
+
const saved = this.savedSnapshot();
|
|
18815
|
+
this.skipDirtyEvaluation = true;
|
|
18816
|
+
this.setValue(cloneDeep(saved ?? []));
|
|
18817
|
+
this.skipDirtyEvaluation = false;
|
|
18818
|
+
this.baselineFingerprint.set(fingerprintAttachments(saved));
|
|
18819
|
+
this.baselinePendingReconcile.set(false);
|
|
18820
|
+
this.statusBeforeDelete.clear();
|
|
18821
|
+
this.setDirty(false);
|
|
18822
|
+
}
|
|
18823
|
+
api() {
|
|
18824
|
+
return {
|
|
18825
|
+
onDirtyChange: this.dirtyChangeSubject,
|
|
18826
|
+
onUploadActionsChanged: this.uploadActionsChangeSubject,
|
|
18827
|
+
getUploadActions: () => this.getUploadActionDescriptors(),
|
|
18828
|
+
runUploadAction: (plugin) => this.runUploadActionByPlugin(plugin),
|
|
18829
|
+
markSaved: () => this.markSaved(),
|
|
18830
|
+
syncBaseline: (saved) => this.syncBaseline(saved),
|
|
18831
|
+
discardToBaseline: () => this.discardToBaseline(),
|
|
18832
|
+
isDirty: () => this.isDirtySignal(),
|
|
18833
|
+
};
|
|
18834
|
+
}
|
|
18835
|
+
/** Current upload actions for host pages (always read via api; list may change after plugins load). */
|
|
18836
|
+
getUploadActionDescriptors() {
|
|
18837
|
+
return this.fileActions().map((action) => ({
|
|
18838
|
+
plugin: action.plugin,
|
|
18839
|
+
text: action.text,
|
|
18840
|
+
textKey: action.textKey,
|
|
18841
|
+
icon: action.icon,
|
|
18842
|
+
}));
|
|
18843
|
+
}
|
|
18844
|
+
async runUploadActionByPlugin(plugin) {
|
|
18845
|
+
if (this.readonly()) {
|
|
18846
|
+
return;
|
|
18847
|
+
}
|
|
18848
|
+
const action = this.fileActions().find((item) => item.plugin === plugin);
|
|
18849
|
+
if (!action) {
|
|
18850
|
+
return;
|
|
18851
|
+
}
|
|
18852
|
+
await action.run(this.capabilities);
|
|
18853
|
+
}
|
|
18854
|
+
notifyUploadActionsChanged() {
|
|
18855
|
+
this.uploadActionsChangeSubject.next();
|
|
18856
|
+
}
|
|
18857
|
+
evaluateDirty() {
|
|
18858
|
+
const attachments = this.getValue() ?? [];
|
|
18859
|
+
const fingerprint = fingerprintAttachments(attachments);
|
|
18860
|
+
const baseline = this.baselineFingerprint();
|
|
18861
|
+
if (this.baselinePendingReconcile()) {
|
|
18862
|
+
this.baselinePendingReconcile.set(false);
|
|
18863
|
+
const normalizedOnly = attachmentsSemanticallyEqual(attachments, this.savedSnapshot());
|
|
18864
|
+
if (normalizedOnly) {
|
|
18865
|
+
this.baselineFingerprint.set(fingerprint);
|
|
18866
|
+
this.setDirty(false);
|
|
18867
|
+
return;
|
|
18868
|
+
}
|
|
18869
|
+
}
|
|
18870
|
+
this.setDirty(fingerprint !== baseline);
|
|
18871
|
+
}
|
|
18872
|
+
setDirty(dirty) {
|
|
18873
|
+
if (this.isDirtySignal() === dirty) {
|
|
18874
|
+
return;
|
|
18875
|
+
}
|
|
18876
|
+
this.isDirtySignal.set(dirty);
|
|
18877
|
+
this.dirtyChangeSubject.next(dirty);
|
|
18878
|
+
}
|
|
18879
|
+
//#endregion
|
|
18880
|
+
async loadActions() {
|
|
18881
|
+
const payload = {
|
|
18882
|
+
plugins: this.plugins() ?? [],
|
|
18883
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18884
|
+
capabilities: this.capabilities,
|
|
18885
|
+
actions: [],
|
|
18886
|
+
options: {
|
|
18887
|
+
multiple: this.multiple(),
|
|
18888
|
+
accept: this.accept(),
|
|
18889
|
+
},
|
|
18890
|
+
};
|
|
18891
|
+
const actions = await this.fileActionsService.getActions(payload);
|
|
18892
|
+
this.innerActions.set(actions);
|
|
18893
|
+
this.notifyUploadActionsChanged();
|
|
18894
|
+
}
|
|
18895
|
+
async configureFromHooks() {
|
|
18896
|
+
const payload = await this.hooks.runAsync(FileUploaderWebhookKeys.Configure, {
|
|
18897
|
+
plugins: this.plugins() ?? [],
|
|
18898
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18899
|
+
capabilities: this.capabilities,
|
|
18900
|
+
overrides: {},
|
|
18901
|
+
});
|
|
18902
|
+
if (payload?.overrides?.accept) {
|
|
18903
|
+
this.acceptOverride.set(payload.overrides.accept);
|
|
18904
|
+
}
|
|
18905
|
+
}
|
|
18906
|
+
/**
|
|
18907
|
+
* Process files from various sources (file picker, drag-drop, etc.)
|
|
18908
|
+
* When showEditDialogAfterSelect is true, the edit dialog (with plugin hooks) is shown per file before adding; otherwise files are added and user can edit via Edit button.
|
|
18909
|
+
*/
|
|
18910
|
+
async processFiles(files) {
|
|
18911
|
+
if (this.readonly()) {
|
|
18912
|
+
return;
|
|
18913
|
+
}
|
|
18914
|
+
if (files.length === 0) {
|
|
18915
|
+
return;
|
|
18916
|
+
}
|
|
18917
|
+
if (!this.multiple()) {
|
|
18918
|
+
this.clear();
|
|
18919
|
+
}
|
|
18920
|
+
const showDialog = this.showEditDialogAfterSelect();
|
|
18921
|
+
const fileUploadRequests = files.map((file) => ({
|
|
18922
|
+
id: AXPDataGenerator.uuid(),
|
|
18923
|
+
name: file.name,
|
|
18924
|
+
size: file.size,
|
|
18925
|
+
type: file.type,
|
|
18926
|
+
status: 'attached',
|
|
18927
|
+
source: {
|
|
18928
|
+
kind: 'blob',
|
|
18929
|
+
value: file,
|
|
18930
|
+
},
|
|
18931
|
+
}));
|
|
18932
|
+
if (showDialog) {
|
|
18933
|
+
await this.addFilesWithEditDialogAsync(fileUploadRequests);
|
|
18934
|
+
return;
|
|
18935
|
+
}
|
|
18936
|
+
// Default: run BeforeFilesAdded (plugins may transform), then add all, then AfterFilesAdded
|
|
18937
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.BeforeFilesAdded, {
|
|
18938
|
+
plugins: this.plugins() ?? [],
|
|
18939
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18940
|
+
capabilities: this.capabilities,
|
|
18941
|
+
newFiles: fileUploadRequests,
|
|
18942
|
+
});
|
|
18943
|
+
this.setValue([...(this.getValue() ?? []), ...fileUploadRequests]);
|
|
18944
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.AfterFilesAdded, {
|
|
18945
|
+
plugins: this.plugins() ?? [],
|
|
18946
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18947
|
+
capabilities: this.capabilities,
|
|
18948
|
+
newFiles: fileUploadRequests,
|
|
18949
|
+
});
|
|
18950
|
+
}
|
|
18951
|
+
/**
|
|
18952
|
+
* Show edit dialog per file and add only those submitted. Used when showEditDialogAfterSelect is true (both from zone and from dropdown actions).
|
|
18953
|
+
*/
|
|
18954
|
+
async addFilesWithEditDialogAsync(fileItems) {
|
|
18955
|
+
const added = [];
|
|
18956
|
+
for (const fileItem of fileItems) {
|
|
18957
|
+
const result = await this.commandExecutor.execute('FileUploader:Edit', {
|
|
18958
|
+
file: fileItem,
|
|
18959
|
+
plugins: this.plugins() ?? [],
|
|
18960
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18961
|
+
enableTitleDescription: this.enableTitleDescription(),
|
|
18962
|
+
isNewFile: true,
|
|
18963
|
+
});
|
|
18964
|
+
if (result?.success && result.data) {
|
|
18965
|
+
added.push(result.data);
|
|
18966
|
+
}
|
|
18967
|
+
}
|
|
18968
|
+
if (added.length > 0) {
|
|
18969
|
+
this.setValue([...(this.getValue() ?? []), ...added]);
|
|
18970
|
+
await this.hooks.runAsync(FileUploaderWebhookKeys.AfterFilesAdded, {
|
|
18971
|
+
plugins: this.plugins() ?? [],
|
|
18972
|
+
excludePlugins: this.excludePlugins() ?? [],
|
|
18973
|
+
capabilities: this.capabilities,
|
|
18974
|
+
newFiles: added,
|
|
18975
|
+
});
|
|
16355
18976
|
}
|
|
16356
18977
|
}
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
18978
|
+
handleFileRemove(file) {
|
|
18979
|
+
this.removeFile(file);
|
|
18980
|
+
}
|
|
18981
|
+
removeFile(file) {
|
|
18982
|
+
if (file.readOnly === true || !file.id) {
|
|
18983
|
+
return;
|
|
16360
18984
|
}
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
return
|
|
16365
|
-
id: ref.refId || ref.sourceKey,
|
|
16366
|
-
text: ref.displayName,
|
|
16367
|
-
};
|
|
18985
|
+
// If multiple is false, always remove the file directly (no redo capability)
|
|
18986
|
+
if (!this.multiple()) {
|
|
18987
|
+
this.setValue((this.getValue() ?? []).filter((f) => f.id !== file.id) ?? []);
|
|
18988
|
+
return;
|
|
16368
18989
|
}
|
|
16369
|
-
// If
|
|
16370
|
-
if (
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
id: ref.id,
|
|
16378
|
-
text: displayText,
|
|
16379
|
-
};
|
|
16380
|
-
}
|
|
16381
|
-
}
|
|
16382
|
-
// Fallback
|
|
16383
|
-
const displayText = ref.displayName || ref.title || ref.id || String(ref);
|
|
16384
|
-
return {
|
|
16385
|
-
id: ref.id,
|
|
16386
|
-
text: displayText,
|
|
16387
|
-
};
|
|
18990
|
+
// If multiple is true, use the existing logic (mark as deleted for redo)
|
|
18991
|
+
if (file.status === 'attached') {
|
|
18992
|
+
this.statusBeforeDelete.delete(file.id);
|
|
18993
|
+
this.setValue((this.getValue() ?? []).filter((f) => f.id !== file.id) ?? []);
|
|
18994
|
+
}
|
|
18995
|
+
else {
|
|
18996
|
+
this.statusBeforeDelete.set(file.id, file.status);
|
|
18997
|
+
this.setValue((this.getValue() ?? []).map((f) => (f.id === file.id ? { ...f, status: 'deleted' } : f)) ?? []);
|
|
16388
18998
|
}
|
|
16389
|
-
// If ref is primitive
|
|
16390
|
-
return {
|
|
16391
|
-
id: String(ref),
|
|
16392
|
-
text: String(ref),
|
|
16393
|
-
};
|
|
16394
18999
|
}
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
19000
|
+
/**
|
|
19001
|
+
* Revert a deleted file back to its pre-delete status.
|
|
19002
|
+
*/
|
|
19003
|
+
handleFileRevert(file) {
|
|
19004
|
+
if (file.readOnly === true || !file.id) {
|
|
19005
|
+
return;
|
|
16398
19006
|
}
|
|
16399
|
-
const
|
|
16400
|
-
if (
|
|
16401
|
-
|
|
16402
|
-
if (formatted) {
|
|
16403
|
-
return formatted;
|
|
16404
|
-
}
|
|
19007
|
+
const restoredStatus = this.resolveRestoredStatus(file);
|
|
19008
|
+
if (!restoredStatus) {
|
|
19009
|
+
return;
|
|
16405
19010
|
}
|
|
16406
|
-
|
|
16407
|
-
|
|
19011
|
+
this.statusBeforeDelete.delete(file.id);
|
|
19012
|
+
this.setValue((this.getValue() ?? []).map((f) => (f.id === file.id ? { ...f, status: restoredStatus } : f)) ?? []);
|
|
16408
19013
|
}
|
|
16409
|
-
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
<div class="ax-flex ax-flex-wrap ax-gap-1">
|
|
16413
|
-
@for (item of visibleItems(); track item.id) {
|
|
16414
|
-
<ax-badge class="ax-p-0.5 ax-accent1" [text]="item.text"></ax-badge>
|
|
19014
|
+
resolveRestoredStatus(file) {
|
|
19015
|
+
if (!file.id) {
|
|
19016
|
+
return undefined;
|
|
16415
19017
|
}
|
|
16416
|
-
|
|
16417
|
-
|
|
19018
|
+
const fileId = file.id;
|
|
19019
|
+
const baselineFile = (this.savedSnapshot() ?? []).find((f) => f.id === fileId);
|
|
19020
|
+
const fromBaseline = baselineFile?.status;
|
|
19021
|
+
if (fromBaseline && fromBaseline !== 'deleted') {
|
|
19022
|
+
return fromBaseline;
|
|
19023
|
+
}
|
|
19024
|
+
const fromDelete = this.statusBeforeDelete.get(fileId);
|
|
19025
|
+
if (fromDelete && fromDelete !== 'deleted') {
|
|
19026
|
+
return fromDelete;
|
|
19027
|
+
}
|
|
19028
|
+
return 'uploaded';
|
|
19029
|
+
}
|
|
19030
|
+
/**
|
|
19031
|
+
* Handle file rename action. Persists name/title/description to file storage immediately when edited
|
|
19032
|
+
* so they are saved even if the entity payload does not carry these fields.
|
|
19033
|
+
*/
|
|
19034
|
+
handleFileRename(file) {
|
|
19035
|
+
if (file.readOnly === true) {
|
|
19036
|
+
return;
|
|
16418
19037
|
}
|
|
19038
|
+
const currentFiles = this.getValue() ?? [];
|
|
19039
|
+
const previousFile = currentFiles.find((f) => f.id === file.id);
|
|
19040
|
+
const nameChanged = previousFile && previousFile.name !== file.name;
|
|
19041
|
+
const titleChanged = previousFile && previousFile.title !== file.title;
|
|
19042
|
+
const descriptionChanged = previousFile && previousFile.description !== file.description;
|
|
19043
|
+
const anyMetadataChanged = nameChanged || titleChanged || descriptionChanged;
|
|
19044
|
+
const shouldChangeStatus = anyMetadataChanged &&
|
|
19045
|
+
previousFile &&
|
|
19046
|
+
(previousFile.status === 'uploaded' || previousFile.status === 'remote');
|
|
19047
|
+
const updatedFile = {
|
|
19048
|
+
...file,
|
|
19049
|
+
status: shouldChangeStatus ? 'editing' : file.status,
|
|
19050
|
+
};
|
|
19051
|
+
this.setValue(currentFiles.map((f) => (f.id === file.id ? updatedFile : f)) ?? []);
|
|
19052
|
+
}
|
|
19053
|
+
clear() {
|
|
19054
|
+
const current = this.getValue() ?? [];
|
|
19055
|
+
const locked = current.filter((f) => f.readOnly === true);
|
|
19056
|
+
this.setValue(locked);
|
|
19057
|
+
}
|
|
19058
|
+
//#region ---- Drag & Drop Handlers ----
|
|
19059
|
+
onFileChange(event) {
|
|
19060
|
+
this.processFiles(event.files);
|
|
19061
|
+
}
|
|
19062
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetEditComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
19063
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPFileUploaderWidgetEditComponent, isStandalone: true, selector: "axp-file-uploader-widget-edit", host: { properties: { "class.axp-file-uploader-widget-edit--borderless": "!showBorder()", "class.ax-block": "true", "class.ax-flex-1": "true", "class.ax-border": "showBorder()", "class.ax-rounded-md": "showBorder()", "class.ax-lightest-surface": "showBorder()" } }, usesInheritance: true, ngImport: i0, template: `
|
|
19064
|
+
@if (showAddItemButton() && !readonly()) {
|
|
19065
|
+
<div class="ax-flex ax-justify-end ax-p-2" [class.ax-border-b]="showBorder()" *translate="let t">
|
|
19066
|
+
<!-- Add Item Button -->
|
|
19067
|
+
<ax-button class="ax-sm" [text]="t('@general:actions.add-item.title') | async" [color]="'primary'">
|
|
19068
|
+
<ax-prefix>
|
|
19069
|
+
<ax-icon icon="fa-light fa-plus"></ax-icon>
|
|
19070
|
+
</ax-prefix>
|
|
19071
|
+
<ax-dropdown-panel>
|
|
19072
|
+
<!-- Upload Dropdown -->
|
|
19073
|
+
<ax-button-item-list>
|
|
19074
|
+
@for (action of fileActions(); track action.plugin) {
|
|
19075
|
+
<ax-button-item
|
|
19076
|
+
(onClick)="action.run(capabilities)"
|
|
19077
|
+
[text]="action.text ?? (action.textKey ? (t(action.textKey) | async)! : '')"
|
|
19078
|
+
>
|
|
19079
|
+
@if (action.icon) {
|
|
19080
|
+
<ax-prefix>
|
|
19081
|
+
<ax-icon [icon]="action.icon"></ax-icon>
|
|
19082
|
+
</ax-prefix>
|
|
19083
|
+
}
|
|
19084
|
+
</ax-button-item>
|
|
19085
|
+
}
|
|
19086
|
+
</ax-button-item-list>
|
|
19087
|
+
</ax-dropdown-panel>
|
|
19088
|
+
</ax-button>
|
|
16419
19089
|
</div>
|
|
16420
|
-
} @else {
|
|
16421
|
-
<span class="ax-text-muted">---</span>
|
|
16422
19090
|
}
|
|
16423
|
-
|
|
19091
|
+
<div
|
|
19092
|
+
[class.ax-p-2]="showBorder()"
|
|
19093
|
+
axUploaderZone
|
|
19094
|
+
(fileChange)="onFileChange($event)"
|
|
19095
|
+
[disableBrowse]="readonly() || ((getValue() ?? []).length) > 0"
|
|
19096
|
+
>
|
|
19097
|
+
<axp-file-list
|
|
19098
|
+
[files]="files()"
|
|
19099
|
+
[readonly]="readonly()"
|
|
19100
|
+
[fileEditable]="fileEditable()"
|
|
19101
|
+
[enableTitleDescription]="enableTitleDescription()"
|
|
19102
|
+
[plugins]="plugins()"
|
|
19103
|
+
[excludePlugins]="excludePlugins()"
|
|
19104
|
+
[capabilities]="capabilities"
|
|
19105
|
+
[multiple]="multiple()"
|
|
19106
|
+
(onRemove)="handleFileRemove($event)"
|
|
19107
|
+
(onRevert)="handleFileRevert($event)"
|
|
19108
|
+
(onRename)="handleFileRename($event)"
|
|
19109
|
+
></axp-file-list>
|
|
19110
|
+
</div>
|
|
19111
|
+
`, isInline: true, styles: [":host{border-color:rgba(var(--ax-comp-editor-border-color))}:host.axp-file-uploader-widget-edit--borderless{border:none!important}.__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: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$1.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: i4$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: i6.AXTranslatorDirective, selector: "[translate]" }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "enableTitleDescription", "multiple", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
16424
19112
|
}
|
|
16425
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
19113
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetEditComponent, decorators: [{
|
|
16426
19114
|
type: Component,
|
|
16427
|
-
args: [{
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
19115
|
+
args: [{ selector: 'axp-file-uploader-widget-edit', template: `
|
|
19116
|
+
@if (showAddItemButton() && !readonly()) {
|
|
19117
|
+
<div class="ax-flex ax-justify-end ax-p-2" [class.ax-border-b]="showBorder()" *translate="let t">
|
|
19118
|
+
<!-- Add Item Button -->
|
|
19119
|
+
<ax-button class="ax-sm" [text]="t('@general:actions.add-item.title') | async" [color]="'primary'">
|
|
19120
|
+
<ax-prefix>
|
|
19121
|
+
<ax-icon icon="fa-light fa-plus"></ax-icon>
|
|
19122
|
+
</ax-prefix>
|
|
19123
|
+
<ax-dropdown-panel>
|
|
19124
|
+
<!-- Upload Dropdown -->
|
|
19125
|
+
<ax-button-item-list>
|
|
19126
|
+
@for (action of fileActions(); track action.plugin) {
|
|
19127
|
+
<ax-button-item
|
|
19128
|
+
(onClick)="action.run(capabilities)"
|
|
19129
|
+
[text]="action.text ?? (action.textKey ? (t(action.textKey) | async)! : '')"
|
|
19130
|
+
>
|
|
19131
|
+
@if (action.icon) {
|
|
19132
|
+
<ax-prefix>
|
|
19133
|
+
<ax-icon [icon]="action.icon"></ax-icon>
|
|
19134
|
+
</ax-prefix>
|
|
19135
|
+
}
|
|
19136
|
+
</ax-button-item>
|
|
19137
|
+
}
|
|
19138
|
+
</ax-button-item-list>
|
|
19139
|
+
</ax-dropdown-panel>
|
|
19140
|
+
</ax-button>
|
|
16438
19141
|
</div>
|
|
16439
|
-
} @else {
|
|
16440
|
-
<span class="ax-text-muted">---</span>
|
|
16441
19142
|
}
|
|
19143
|
+
<div
|
|
19144
|
+
[class.ax-p-2]="showBorder()"
|
|
19145
|
+
axUploaderZone
|
|
19146
|
+
(fileChange)="onFileChange($event)"
|
|
19147
|
+
[disableBrowse]="readonly() || ((getValue() ?? []).length) > 0"
|
|
19148
|
+
>
|
|
19149
|
+
<axp-file-list
|
|
19150
|
+
[files]="files()"
|
|
19151
|
+
[readonly]="readonly()"
|
|
19152
|
+
[fileEditable]="fileEditable()"
|
|
19153
|
+
[enableTitleDescription]="enableTitleDescription()"
|
|
19154
|
+
[plugins]="plugins()"
|
|
19155
|
+
[excludePlugins]="excludePlugins()"
|
|
19156
|
+
[capabilities]="capabilities"
|
|
19157
|
+
[multiple]="multiple()"
|
|
19158
|
+
(onRemove)="handleFileRemove($event)"
|
|
19159
|
+
(onRevert)="handleFileRevert($event)"
|
|
19160
|
+
(onRename)="handleFileRename($event)"
|
|
19161
|
+
></axp-file-list>
|
|
19162
|
+
</div>
|
|
19163
|
+
`, host: {
|
|
19164
|
+
'[class.axp-file-uploader-widget-edit--borderless]': '!showBorder()',
|
|
19165
|
+
'[class.ax-block]': 'true',
|
|
19166
|
+
'[class.ax-flex-1]': 'true',
|
|
19167
|
+
'[class.ax-border]': 'showBorder()',
|
|
19168
|
+
'[class.ax-rounded-md]': 'showBorder()',
|
|
19169
|
+
'[class.ax-lightest-surface]': 'showBorder()',
|
|
19170
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
19171
|
+
CommonModule,
|
|
19172
|
+
FormsModule,
|
|
19173
|
+
AXButtonModule,
|
|
19174
|
+
AXUploaderZoneDirective,
|
|
19175
|
+
AXDecoratorModule,
|
|
19176
|
+
AXLoadingModule,
|
|
19177
|
+
AXDropdownModule,
|
|
19178
|
+
AXPComponentSlotModule,
|
|
19179
|
+
AXTranslationModule,
|
|
19180
|
+
AXPFileListComponent,
|
|
19181
|
+
], styles: [":host{border-color:rgba(var(--ax-comp-editor-border-color))}:host.axp-file-uploader-widget-edit--borderless{border:none!important}.__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"] }]
|
|
19182
|
+
}] });
|
|
19183
|
+
|
|
19184
|
+
var fileUploaderWidgetEdit_component = /*#__PURE__*/Object.freeze({
|
|
19185
|
+
__proto__: null,
|
|
19186
|
+
AXPFileUploaderWidgetEditComponent: AXPFileUploaderWidgetEditComponent
|
|
19187
|
+
});
|
|
19188
|
+
|
|
19189
|
+
class AXPFileUploaderWidgetViewComponent extends AXPValueWidgetComponent {
|
|
19190
|
+
constructor() {
|
|
19191
|
+
super(...arguments);
|
|
19192
|
+
this.files = computed(() => (this.getValue() ?? []).filter(file => file.status !== 'deleted') ?? [], ...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
|
|
19193
|
+
}
|
|
19194
|
+
get __class() {
|
|
19195
|
+
const cls = {};
|
|
19196
|
+
cls[`ax-block`] = true;
|
|
19197
|
+
cls[`ax-flex-1`] = true;
|
|
19198
|
+
return cls;
|
|
19199
|
+
}
|
|
19200
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
19201
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPFileUploaderWidgetViewComponent, isStandalone: true, selector: "axp-file-uploader-widget-view", host: { properties: { "class": "this.__class" } }, usesInheritance: true, ngImport: i0, template: `
|
|
19202
|
+
<axp-file-list [files]="files()" [readonly]="true"></axp-file-list>
|
|
19203
|
+
`, 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", "enableTitleDescription", "multiple", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
19204
|
+
}
|
|
19205
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetViewComponent, decorators: [{
|
|
19206
|
+
type: Component,
|
|
19207
|
+
args: [{
|
|
19208
|
+
selector: 'axp-file-uploader-widget-view',
|
|
19209
|
+
template: `
|
|
19210
|
+
<axp-file-list [files]="files()" [readonly]="true"></axp-file-list>
|
|
16442
19211
|
`,
|
|
16443
19212
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
16444
|
-
|
|
16445
|
-
|
|
19213
|
+
standalone: true,
|
|
19214
|
+
imports: [
|
|
19215
|
+
FormsModule,
|
|
19216
|
+
AXButtonModule,
|
|
19217
|
+
AXDecoratorModule,
|
|
19218
|
+
AXUploaderModule,
|
|
19219
|
+
AXLoadingModule,
|
|
19220
|
+
AXTranslationModule,
|
|
19221
|
+
AXPFileListComponent
|
|
19222
|
+
],
|
|
19223
|
+
inputs: [],
|
|
16446
19224
|
}]
|
|
16447
|
-
}],
|
|
19225
|
+
}], propDecorators: { __class: [{
|
|
19226
|
+
type: HostBinding,
|
|
19227
|
+
args: ['class']
|
|
19228
|
+
}] } });
|
|
16448
19229
|
|
|
16449
|
-
var
|
|
19230
|
+
var fileUploaderWidgetView_component = /*#__PURE__*/Object.freeze({
|
|
16450
19231
|
__proto__: null,
|
|
16451
|
-
|
|
19232
|
+
AXPFileUploaderWidgetViewComponent: AXPFileUploaderWidgetViewComponent
|
|
19233
|
+
});
|
|
19234
|
+
|
|
19235
|
+
const AXPFileUploaderWidget = {
|
|
19236
|
+
name: 'attachments',
|
|
19237
|
+
title: '@platform-layout-widgets:widgets.attachments.title',
|
|
19238
|
+
description: '@platform-layout-widgets:widgets.attachments.description',
|
|
19239
|
+
icon: 'fa-light fa-files',
|
|
19240
|
+
categories: [AXP_WIDGETS_ADVANCE_CATEGORY],
|
|
19241
|
+
subCategory: AXP_WIDGETS_ADVANCE_SUB_MEDIA,
|
|
19242
|
+
groups: [AXPWidgetGroupEnum.EntityWidget, 'form-element'],
|
|
19243
|
+
type: 'editor',
|
|
19244
|
+
properties: [
|
|
19245
|
+
AXP_NAME_PROPERTY,
|
|
19246
|
+
AXP_DATA_PATH_PROPERTY,
|
|
19247
|
+
AXP_ALLOW_MULTIPLE_PROPERTY,
|
|
19248
|
+
AXP_DOWNLOADABLE_PROPERTY,
|
|
19249
|
+
AXP_READONLY_PROPERTY,
|
|
19250
|
+
AXP_DESCRIPTION_PROPERTY,
|
|
19251
|
+
createStringProperty({
|
|
19252
|
+
name: 'accept',
|
|
19253
|
+
title: 'Accept',
|
|
19254
|
+
path: 'options.accept',
|
|
19255
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19256
|
+
}),
|
|
19257
|
+
createBooleanProperty({
|
|
19258
|
+
name: 'fileEditable',
|
|
19259
|
+
title: 'File Editable',
|
|
19260
|
+
path: 'options.fileEditable',
|
|
19261
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19262
|
+
}),
|
|
19263
|
+
createBooleanProperty({
|
|
19264
|
+
name: 'enableTitleDescription',
|
|
19265
|
+
title: 'Enable Title & Description',
|
|
19266
|
+
path: 'options.enableTitleDescription',
|
|
19267
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19268
|
+
}),
|
|
19269
|
+
createBooleanProperty({
|
|
19270
|
+
name: 'showEditDialogAfterSelect',
|
|
19271
|
+
title: 'Show Edit Dialog After File Select',
|
|
19272
|
+
path: 'options.showEditDialogAfterSelect',
|
|
19273
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19274
|
+
}),
|
|
19275
|
+
createBooleanProperty({
|
|
19276
|
+
name: 'showBorder',
|
|
19277
|
+
title: 'Show Border',
|
|
19278
|
+
path: 'options.showBorder',
|
|
19279
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19280
|
+
}),
|
|
19281
|
+
createBooleanProperty({
|
|
19282
|
+
name: 'showAddItemButton',
|
|
19283
|
+
title: 'Show Add Item Button',
|
|
19284
|
+
path: 'options.showAddItemButton',
|
|
19285
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19286
|
+
}),
|
|
19287
|
+
createNumberProperty({
|
|
19288
|
+
name: 'maxFileSize',
|
|
19289
|
+
title: 'Max File Size (bytes)',
|
|
19290
|
+
path: 'options.maxFileSize',
|
|
19291
|
+
group: AXP_BEHAVIOR_PROPERTY_GROUP,
|
|
19292
|
+
}),
|
|
19293
|
+
],
|
|
19294
|
+
components: {
|
|
19295
|
+
view: {
|
|
19296
|
+
component: () => Promise.resolve().then(function () { return fileUploaderWidgetView_component; }).then((c) => c.AXPFileUploaderWidgetViewComponent),
|
|
19297
|
+
},
|
|
19298
|
+
edit: {
|
|
19299
|
+
component: () => Promise.resolve().then(function () { return fileUploaderWidgetEdit_component; }).then((c) => c.AXPFileUploaderWidgetEditComponent),
|
|
19300
|
+
},
|
|
19301
|
+
column: {
|
|
19302
|
+
component: () => Promise.resolve().then(function () { return fileUploaderWidgetColumn_component; }).then((c) => c.AXPFileUploaderWidgetColumnComponent),
|
|
19303
|
+
},
|
|
19304
|
+
designer: {
|
|
19305
|
+
component: () => Promise.resolve().then(function () { return fileUploaderWidgetEdit_component; }).then((c) => c.AXPFileUploaderWidgetEditComponent),
|
|
19306
|
+
},
|
|
19307
|
+
},
|
|
19308
|
+
};
|
|
19309
|
+
|
|
19310
|
+
var index = /*#__PURE__*/Object.freeze({
|
|
19311
|
+
__proto__: null,
|
|
19312
|
+
AXPEditFileUploaderCommand: AXPEditFileUploaderCommand,
|
|
19313
|
+
AXPFileListComponent: AXPFileListComponent,
|
|
19314
|
+
AXPFileUploaderLoadFilesQuery: AXPFileUploaderLoadFilesQuery,
|
|
19315
|
+
AXPFileUploaderSaveFilesCommand: AXPFileUploaderSaveFilesCommand,
|
|
19316
|
+
AXPFileUploaderWidget: AXPFileUploaderWidget,
|
|
19317
|
+
AXPFileUploaderWidgetColumnComponent: AXPFileUploaderWidgetColumnComponent,
|
|
19318
|
+
AXPFileUploaderWidgetEditComponent: AXPFileUploaderWidgetEditComponent,
|
|
19319
|
+
AXPFileUploaderWidgetService: AXPFileUploaderWidgetService,
|
|
19320
|
+
AXPFileUploaderWidgetViewComponent: AXPFileUploaderWidgetViewComponent,
|
|
19321
|
+
attachmentFieldCount: attachmentFieldCount,
|
|
19322
|
+
attachmentsSemanticallyEqual: attachmentsSemanticallyEqual,
|
|
19323
|
+
committedAttachments: committedAttachments,
|
|
19324
|
+
fingerprintAttachmentItem: fingerprintAttachmentItem,
|
|
19325
|
+
fingerprintAttachments: fingerprintAttachments,
|
|
19326
|
+
hasFileUploaderTitleOrDescriptionFields: hasFileUploaderTitleOrDescriptionFields,
|
|
19327
|
+
isAttachmentListEntry: isAttachmentListEntry,
|
|
19328
|
+
isFileListItem: isFileListItem,
|
|
19329
|
+
isFileUploaderEditDialogAuto: isFileUploaderEditDialogAuto,
|
|
19330
|
+
normalizeEntityFieldToFileList: normalizeEntityFieldToFileList,
|
|
19331
|
+
persistedAttachments: persistedAttachments,
|
|
19332
|
+
resolveFileUploaderEditDialog: resolveFileUploaderEditDialog,
|
|
19333
|
+
resolveFileUploaderEntityScope: resolveFileUploaderEntityScope
|
|
16452
19334
|
});
|
|
16453
19335
|
|
|
16454
19336
|
//#region ---- Imports ----
|
|
@@ -17697,6 +20579,7 @@ const ENTITY_WIDGETS = [
|
|
|
17697
20579
|
AXPEntityCategoryWidget,
|
|
17698
20580
|
AXPMultiSourceSelectorWidget,
|
|
17699
20581
|
AXPEntityDefinitionProviderWidget,
|
|
20582
|
+
AXPFileUploaderWidget,
|
|
17700
20583
|
];
|
|
17701
20584
|
const ENTITY_EXTENDED_WIDGETS = [
|
|
17702
20585
|
{
|
|
@@ -20402,6 +23285,127 @@ const defaultMultiLanguageMiddlewareProvider = {
|
|
|
20402
23285
|
};
|
|
20403
23286
|
//#endregion
|
|
20404
23287
|
|
|
23288
|
+
//#region ---- Smart card layout builders ----
|
|
23289
|
+
const TITLE_FIELD_CANDIDATES = ['title', 'name'];
|
|
23290
|
+
const DESCRIPTION_FIELD_CANDIDATES = ['description'];
|
|
23291
|
+
const ICON_FIELD_CANDIDATES = ['icon'];
|
|
23292
|
+
const STATUS_FIELD_CANDIDATES = ['status', 'statusId'];
|
|
23293
|
+
function findPropertyName(properties, candidates) {
|
|
23294
|
+
const names = new Set(properties.map((p) => p.name));
|
|
23295
|
+
return candidates.find((candidate) => names.has(candidate));
|
|
23296
|
+
}
|
|
23297
|
+
function findColumnName(columnNames, candidates) {
|
|
23298
|
+
return candidates.find((candidate) => columnNames.includes(candidate));
|
|
23299
|
+
}
|
|
23300
|
+
function findNestedColumnName(columnNames, suffix) {
|
|
23301
|
+
return columnNames.find((name) => name === suffix || name.endsWith(`.${suffix}`));
|
|
23302
|
+
}
|
|
23303
|
+
function resolveTitleField(properties, columnNames) {
|
|
23304
|
+
return (findPropertyName(properties, TITLE_FIELD_CANDIDATES) ??
|
|
23305
|
+
findColumnName(columnNames, TITLE_FIELD_CANDIDATES) ??
|
|
23306
|
+
findNestedColumnName(columnNames, 'fullName') ??
|
|
23307
|
+
findNestedColumnName(columnNames, 'displayName') ??
|
|
23308
|
+
columnNames[0] ??
|
|
23309
|
+
'name');
|
|
23310
|
+
}
|
|
23311
|
+
function resolveDescriptionField(properties, columnNames) {
|
|
23312
|
+
return (findPropertyName(properties, DESCRIPTION_FIELD_CANDIDATES) ??
|
|
23313
|
+
findColumnName(columnNames, DESCRIPTION_FIELD_CANDIDATES) ??
|
|
23314
|
+
findNestedColumnName(columnNames, 'description'));
|
|
23315
|
+
}
|
|
23316
|
+
function resolveIconField(properties, columnNames) {
|
|
23317
|
+
return findPropertyName(properties, ICON_FIELD_CANDIDATES) ?? findColumnName(columnNames, ICON_FIELD_CANDIDATES);
|
|
23318
|
+
}
|
|
23319
|
+
function resolveStatusField(properties, columnNames) {
|
|
23320
|
+
return (findPropertyName(properties, STATUS_FIELD_CANDIDATES) ??
|
|
23321
|
+
findColumnName(columnNames, STATUS_FIELD_CANDIDATES) ??
|
|
23322
|
+
findNestedColumnName(columnNames, 'status'));
|
|
23323
|
+
}
|
|
23324
|
+
function resolveListColumnNames(entityColumns, tableColumns) {
|
|
23325
|
+
const names = new Set();
|
|
23326
|
+
const ordered = [];
|
|
23327
|
+
const append = (columns) => {
|
|
23328
|
+
for (const column of columns ?? []) {
|
|
23329
|
+
if (!column.name || names.has(column.name)) {
|
|
23330
|
+
continue;
|
|
23331
|
+
}
|
|
23332
|
+
if (column.options?.visible === false) {
|
|
23333
|
+
continue;
|
|
23334
|
+
}
|
|
23335
|
+
names.add(column.name);
|
|
23336
|
+
ordered.push(column.name);
|
|
23337
|
+
}
|
|
23338
|
+
};
|
|
23339
|
+
append(entityColumns);
|
|
23340
|
+
append(tableColumns);
|
|
23341
|
+
return ordered;
|
|
23342
|
+
}
|
|
23343
|
+
function buildCardFields(columnNames, headerFields, statusField) {
|
|
23344
|
+
return columnNames
|
|
23345
|
+
.filter((name) => !headerFields.has(name))
|
|
23346
|
+
.map((name) => {
|
|
23347
|
+
const field = { name };
|
|
23348
|
+
if (statusField && name === statusField) {
|
|
23349
|
+
field.display = 'badge';
|
|
23350
|
+
}
|
|
23351
|
+
return field;
|
|
23352
|
+
});
|
|
23353
|
+
}
|
|
23354
|
+
function buildDefaultCardLayout(entity, entityColumns) {
|
|
23355
|
+
const properties = entity.properties ?? [];
|
|
23356
|
+
const tableColumns = entity.interfaces?.master?.list?.layouts?.table?.columns;
|
|
23357
|
+
const columnNames = resolveListColumnNames(entityColumns, tableColumns);
|
|
23358
|
+
if (columnNames.length === 0) {
|
|
23359
|
+
return null;
|
|
23360
|
+
}
|
|
23361
|
+
const titleField = resolveTitleField(properties, columnNames);
|
|
23362
|
+
const descriptionField = resolveDescriptionField(properties, columnNames);
|
|
23363
|
+
const iconField = resolveIconField(properties, columnNames);
|
|
23364
|
+
const statusField = resolveStatusField(properties, columnNames);
|
|
23365
|
+
const headerFields = new Set([titleField, descriptionField, iconField].filter((field) => !!field));
|
|
23366
|
+
const fields = buildCardFields(columnNames, headerFields, statusField);
|
|
23367
|
+
if (fields.length === 0 && !titleField) {
|
|
23368
|
+
return null;
|
|
23369
|
+
}
|
|
23370
|
+
return {
|
|
23371
|
+
enabled: true,
|
|
23372
|
+
header: {
|
|
23373
|
+
title: titleField,
|
|
23374
|
+
...(descriptionField ? { description: descriptionField } : {}),
|
|
23375
|
+
...(iconField ? { icon: iconField } : {}),
|
|
23376
|
+
},
|
|
23377
|
+
fields,
|
|
23378
|
+
};
|
|
23379
|
+
}
|
|
23380
|
+
//#endregion
|
|
23381
|
+
//#region ---- Default card layout middleware ----
|
|
23382
|
+
/**
|
|
23383
|
+
* When an entity has no `interfaces.master.list.layouts.card`, creates a sensible default
|
|
23384
|
+
* from list columns and property names (title, description, remaining columns as card fields).
|
|
23385
|
+
*/
|
|
23386
|
+
const defaultCardLayoutMiddleware = (context) => {
|
|
23387
|
+
const existingCard = context.interfaces.master.list.card.get();
|
|
23388
|
+
if (existingCard !== undefined) {
|
|
23389
|
+
return;
|
|
23390
|
+
}
|
|
23391
|
+
if (!context.interfaces.master.list.get()) {
|
|
23392
|
+
return;
|
|
23393
|
+
}
|
|
23394
|
+
const layout = buildDefaultCardLayout(context.entity, context.columns.list());
|
|
23395
|
+
if (!layout) {
|
|
23396
|
+
return;
|
|
23397
|
+
}
|
|
23398
|
+
context.interfaces.master.list.card.set(layout);
|
|
23399
|
+
};
|
|
23400
|
+
//#endregion
|
|
23401
|
+
//#region ---- Provider registration ----
|
|
23402
|
+
const defaultCardLayoutMiddlewareProvider = {
|
|
23403
|
+
entityName: '*',
|
|
23404
|
+
modifier: defaultCardLayoutMiddleware,
|
|
23405
|
+
order: 600,
|
|
23406
|
+
};
|
|
23407
|
+
//#endregion
|
|
23408
|
+
|
|
20405
23409
|
/**
|
|
20406
23410
|
* Factory to create a column width middleware using provided config map.
|
|
20407
23411
|
* Sets width for columns defined in the map if not already defined on the column.
|
|
@@ -20456,8 +23460,9 @@ const AXPCrudModifier = {
|
|
|
20456
23460
|
if (!queries?.list) {
|
|
20457
23461
|
queries.list = {
|
|
20458
23462
|
execute: async (e) => {
|
|
20459
|
-
|
|
20460
|
-
|
|
23463
|
+
const res = await dataService.query(e);
|
|
23464
|
+
console.log('query', res, ctx.module.get() + '.' + ctx.name.get(), e);
|
|
23465
|
+
return res;
|
|
20461
23466
|
},
|
|
20462
23467
|
type: AXPEntityQueryType.List,
|
|
20463
23468
|
};
|
|
@@ -21074,54 +24079,50 @@ const AXPShowDetailsViewWorkflow = {
|
|
|
21074
24079
|
class AXPShowFileUploaderPopupAction extends AXPWorkflowAction {
|
|
21075
24080
|
constructor() {
|
|
21076
24081
|
super(...arguments);
|
|
24082
|
+
//#region ---- Services & Dependencies ----
|
|
21077
24083
|
this.fileUploaderWidgetService = inject(AXPFileUploaderWidgetService);
|
|
21078
|
-
this.entityRegistryService = inject(AXPEntityDefinitionRegistryService);
|
|
21079
24084
|
}
|
|
24085
|
+
//#endregion
|
|
24086
|
+
//#region ---- Workflow Action ----
|
|
21080
24087
|
async execute(context) {
|
|
21081
|
-
|
|
21082
|
-
const key = context.getVariable('options.key');
|
|
24088
|
+
const field = context.getVariable('options.key');
|
|
21083
24089
|
const rawReadonly = context.getVariable('options.readonly') ??
|
|
21084
24090
|
context.getVariable('options.readOnly');
|
|
21085
24091
|
const isReadonly = rawReadonly === true || rawReadonly === 'true';
|
|
21086
24092
|
const multiple = context.getVariable('options.multiple');
|
|
21087
24093
|
const accept = context.getVariable('options.accept');
|
|
21088
24094
|
const maxFileSize = context.getVariable('options.maxFileSize');
|
|
21089
|
-
const files = context.getVariable('options.files');
|
|
21090
24095
|
const fileEditable = context.getVariable('options.fileEditable');
|
|
21091
|
-
const
|
|
21092
|
-
const
|
|
21093
|
-
const
|
|
21094
|
-
|
|
21095
|
-
const
|
|
21096
|
-
|
|
24096
|
+
const showAddItemButton = context.getVariable('options.showAddItemButton');
|
|
24097
|
+
const editDialog = context.getVariable('options.editDialog');
|
|
24098
|
+
const plugins = context.getVariable('options.plugins');
|
|
24099
|
+
const entityOption = context.getVariable('options.entity');
|
|
24100
|
+
const entityRef = entityOption?.name ??
|
|
24101
|
+
context.getVariable('entity') ??
|
|
24102
|
+
context.getVariable('options.entityName');
|
|
24103
|
+
const entityId = entityOption?.id ?? context.getVariable('options.id');
|
|
24104
|
+
const resolvedField = entityOption?.field ?? field;
|
|
24105
|
+
if (!entityRef || !entityId || !resolvedField) {
|
|
24106
|
+
context.setOutput('result', false);
|
|
24107
|
+
return;
|
|
24108
|
+
}
|
|
24109
|
+
const files = await this.fileUploaderWidgetService.showFileList({
|
|
24110
|
+
entity: { name: entityRef, id: entityId, field: resolvedField },
|
|
21097
24111
|
readonly: isReadonly,
|
|
21098
|
-
multiple
|
|
21099
|
-
accept
|
|
21100
|
-
fileEditable
|
|
21101
|
-
|
|
21102
|
-
|
|
21103
|
-
|
|
24112
|
+
multiple,
|
|
24113
|
+
accept,
|
|
24114
|
+
fileEditable,
|
|
24115
|
+
maxFileSize,
|
|
24116
|
+
showAddItemButton: showAddItemButton ?? true,
|
|
24117
|
+
plugins: plugins ?? [],
|
|
24118
|
+
editDialog,
|
|
21104
24119
|
});
|
|
21105
|
-
|
|
21106
|
-
if (!res) {
|
|
24120
|
+
if (!files) {
|
|
21107
24121
|
context.setOutput('result', false);
|
|
21108
24122
|
return;
|
|
21109
24123
|
}
|
|
21110
|
-
|
|
21111
|
-
|
|
21112
|
-
const entityDefinition = await this.entityRegistryService.resolve(module, entity);
|
|
21113
|
-
if (entityDefinition) {
|
|
21114
|
-
const updateExec = entityDefinition.commands?.update?.execute;
|
|
21115
|
-
const entityData = await updateExec({
|
|
21116
|
-
id: context.getVariable('options.id'),
|
|
21117
|
-
[key]: res,
|
|
21118
|
-
});
|
|
21119
|
-
context.setOutput('result', true);
|
|
21120
|
-
// Merge full file list (name, title, description) from popup into returned data so
|
|
21121
|
-
// workflow output includes title/description when enableTitleDescription is used
|
|
21122
|
-
const dataWithFiles = { ...entityData, [key]: res };
|
|
21123
|
-
context.setVariable('data', cloneDeep(dataWithFiles));
|
|
21124
|
-
}
|
|
24124
|
+
context.setOutput('result', true);
|
|
24125
|
+
context.setVariable('data', cloneDeep({ id: entityId, [resolvedField]: files }));
|
|
21125
24126
|
}
|
|
21126
24127
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPShowFileUploaderPopupAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
21127
24128
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPShowFileUploaderPopupAction }); }
|
|
@@ -21291,7 +24292,8 @@ function routesFacory() {
|
|
|
21291
24292
|
}
|
|
21292
24293
|
class AXPEntityModule {
|
|
21293
24294
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
21294
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityModule, imports: [RouterModule,
|
|
24295
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityModule, imports: [RouterModule,
|
|
24296
|
+
AXPAttachmentsPluginModule, i1$3.AXPWorkflowModule, AXPWidgetCoreModule,
|
|
21295
24297
|
LayoutBuilderModule] }); }
|
|
21296
24298
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityModule, providers: [
|
|
21297
24299
|
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXPEntityWidgetsProvider, multi: true },
|
|
@@ -21322,6 +24324,11 @@ class AXPEntityModule {
|
|
|
21322
24324
|
useValue: columnOrderingMiddlewareProvider,
|
|
21323
24325
|
multi: true,
|
|
21324
24326
|
},
|
|
24327
|
+
{
|
|
24328
|
+
provide: AXP_ENTITY_MODIFIER,
|
|
24329
|
+
useValue: defaultCardLayoutMiddlewareProvider,
|
|
24330
|
+
multi: true,
|
|
24331
|
+
},
|
|
21325
24332
|
{
|
|
21326
24333
|
provide: AXP_ENTITY_MODIFIER,
|
|
21327
24334
|
useValue: AXPCrudModifier,
|
|
@@ -21386,14 +24393,27 @@ class AXPEntityModule {
|
|
|
21386
24393
|
key: 'Entity:View',
|
|
21387
24394
|
command: () => Promise.resolve().then(function () { return viewEntityDetails_command; }).then((c) => c.AXPViewEntityDetailsCommand),
|
|
21388
24395
|
},
|
|
24396
|
+
{
|
|
24397
|
+
key: 'FileUploader:Edit',
|
|
24398
|
+
command: () => Promise.resolve().then(function () { return index; }).then((c) => c.AXPEditFileUploaderCommand),
|
|
24399
|
+
},
|
|
24400
|
+
{
|
|
24401
|
+
key: 'FileUploader:SaveFiles',
|
|
24402
|
+
command: () => Promise.resolve().then(function () { return index; }).then((c) => c.AXPFileUploaderSaveFilesCommand),
|
|
24403
|
+
},
|
|
21389
24404
|
]),
|
|
21390
24405
|
provideQuerySetups([
|
|
21391
24406
|
{
|
|
21392
24407
|
key: 'Entity:GetDetails',
|
|
21393
24408
|
loader: () => Promise.resolve().then(function () { return getEntityDetails_query; }).then((c) => c.AXPGetEntityDetailsQuery),
|
|
21394
24409
|
},
|
|
24410
|
+
{
|
|
24411
|
+
key: 'FileUploader:LoadFiles',
|
|
24412
|
+
loader: () => Promise.resolve().then(function () { return index; }).then((c) => c.AXPFileUploaderLoadFilesQuery),
|
|
24413
|
+
},
|
|
21395
24414
|
]),
|
|
21396
24415
|
], imports: [RouterModule,
|
|
24416
|
+
AXPAttachmentsPluginModule,
|
|
21397
24417
|
AXPWorkflowModule.forChild({
|
|
21398
24418
|
actions: {
|
|
21399
24419
|
AXPEntityCreatePopupAction,
|
|
@@ -21429,6 +24449,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
21429
24449
|
args: [{
|
|
21430
24450
|
imports: [
|
|
21431
24451
|
RouterModule,
|
|
24452
|
+
AXPAttachmentsPluginModule,
|
|
21432
24453
|
AXPWorkflowModule.forChild({
|
|
21433
24454
|
actions: {
|
|
21434
24455
|
AXPEntityCreatePopupAction,
|
|
@@ -21490,6 +24511,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
21490
24511
|
useValue: columnOrderingMiddlewareProvider,
|
|
21491
24512
|
multi: true,
|
|
21492
24513
|
},
|
|
24514
|
+
{
|
|
24515
|
+
provide: AXP_ENTITY_MODIFIER,
|
|
24516
|
+
useValue: defaultCardLayoutMiddlewareProvider,
|
|
24517
|
+
multi: true,
|
|
24518
|
+
},
|
|
21493
24519
|
{
|
|
21494
24520
|
provide: AXP_ENTITY_MODIFIER,
|
|
21495
24521
|
useValue: AXPCrudModifier,
|
|
@@ -21554,12 +24580,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
21554
24580
|
key: 'Entity:View',
|
|
21555
24581
|
command: () => Promise.resolve().then(function () { return viewEntityDetails_command; }).then((c) => c.AXPViewEntityDetailsCommand),
|
|
21556
24582
|
},
|
|
24583
|
+
{
|
|
24584
|
+
key: 'FileUploader:Edit',
|
|
24585
|
+
command: () => Promise.resolve().then(function () { return index; }).then((c) => c.AXPEditFileUploaderCommand),
|
|
24586
|
+
},
|
|
24587
|
+
{
|
|
24588
|
+
key: 'FileUploader:SaveFiles',
|
|
24589
|
+
command: () => Promise.resolve().then(function () { return index; }).then((c) => c.AXPFileUploaderSaveFilesCommand),
|
|
24590
|
+
},
|
|
21557
24591
|
]),
|
|
21558
24592
|
provideQuerySetups([
|
|
21559
24593
|
{
|
|
21560
24594
|
key: 'Entity:GetDetails',
|
|
21561
24595
|
loader: () => Promise.resolve().then(function () { return getEntityDetails_query; }).then((c) => c.AXPGetEntityDetailsQuery),
|
|
21562
24596
|
},
|
|
24597
|
+
{
|
|
24598
|
+
key: 'FileUploader:LoadFiles',
|
|
24599
|
+
loader: () => Promise.resolve().then(function () { return index; }).then((c) => c.AXPFileUploaderLoadFilesQuery),
|
|
24600
|
+
},
|
|
21563
24601
|
]),
|
|
21564
24602
|
],
|
|
21565
24603
|
}]
|
|
@@ -21800,5 +24838,5 @@ var getEntityDetails_query = /*#__PURE__*/Object.freeze({
|
|
|
21800
24838
|
* Generated bundle index. Do not edit.
|
|
21801
24839
|
*/
|
|
21802
24840
|
|
|
21803
|
-
export { AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListPersistenceModeDefault, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, AXPGetEntityDetailsQuery, AXPLayoutOrderingConfigService, AXPLookupWidget, AXPLookupWidgetColumnComponent, AXPLookupWidgetEditComponent, AXPLookupWidgetViewComponent, AXPMiddlewareAbortError, AXPMiddlewareEntityStorageService, AXPModifyEntitySectionWorkflow, AXPMultiSourceDefinitionProviderContext, AXPMultiSourceDefinitionProviderService, AXPMultiSourceFederatedSearchService, AXPMultiSourceSelectorComponent, AXPMultiSourceSelectorService, AXPMultiSourceSelectorWidget, AXPMultiSourceSelectorWidgetColumnComponent, AXPMultiSourceSelectorWidgetEditComponent, AXPMultiSourceSelectorWidgetViewComponent, AXPMultiSourceType, AXPOpenEntityDetailsCommand, AXPQuickEntityModifyPopupAction, AXPQuickModifyEntityWorkflow, AXPRelatedColumnEnrichmentService, AXPRelatedColumnMetadataResolver, AXPSelectorStructureWidget, AXPSelectorStructureWidgetColumnComponent, AXPSelectorStructureWidgetEditComponent, AXPSelectorStructureWidgetViewComponent, AXPShowDetailViewAction, AXPShowDetailsViewWorkflow, AXPShowListViewAction, AXPShowListViewWorkflow, AXPTruncatedBreadcrumbComponent, AXPUpdateEntityCommand, AXPViewEntityDetailsCommand, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXP_DATA_SEEDER_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER, AXP_ENTITY_STORAGE_BACKEND, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_MULTI_SOURCE_DEFINITION_PROVIDER, AXP_RECORD_WORKFLOW_INFO_CORRELATION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_DEFINITION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_INSTANCE_ID_FIELD, DEFAULT_COLUMN_ORDER, DEFAULT_PAIR_SPAN_RULES, DEFAULT_PROPERTY_ORDER, DEFAULT_SECTION_ORDER, ENTITY_LIST_ROUTE_CONTEXT_SESSION_KEY, EntityBuilder, EntityDataAccessor, actionExists, applyDataSourcePagingWithoutLoad, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, buildAXPRecordWorkflowInfo, canPersistEntityListState, cloneLayoutArrays, collectEntityQuickSearchFieldPaths, collectNestedCreateHiddenProperties, collectNestedFieldPathsFromEntityColumns, collectQuickSearchPathsFromSingleEntityDefinition, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, computeEntityAggregates, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, defaultMultiLanguageMiddleware, defaultMultiLanguageMiddlewareProvider, detectEntityChanges, ensureLayoutPropertyView, ensureLayoutSection, ensureListActions, entityDetailsCreateActions, entityDetailsCreateActionsDeferredParent, entityDetailsCrudActions, entityDetailsEditAction, entityDetailsNewEditAction, entityDetailsReferenceCondition, entityDetailsReferenceCreateActions, entityDetailsSimpleCondition, entityMasterBulkDeleteAction, entityMasterCreateAction, entityMasterCrudActions, entityMasterDeleteAction, entityMasterEditAction, entityMasterRecordActions, entityMasterViewAction, entityOverrideDetailsViewAction, eventDispatchMiddleware, filterSortEntityRows, findEntityListRowDataInTree, formatLookupItemDisplay, getDataSourcePageIndex, getEntityListRowId, getMasterInterfacePropertySortKey, getRecordWorkflowCorrelationId, getRecordWorkflowInstanceId, isAXPMiddlewareAbortError, isCategoryEntity, isCategoryFilter, isUnresolvedLookupDisplayTemplate, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mergeForeignKeyFieldIntoCreateActions, normalizeEntityListPersistenceMode, normalizeListPaging, normalizeLookupDisplayTemplate, provideEntity, resolveEntityPluginDetailPageOrder, resolveLookupDisplayField, resolveLookupDisplayTemplate, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
|
|
24841
|
+
export { ATTACHMENTS_PAGE_COMPONENT_KEY, AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEditFileUploaderCommand, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorRowActionsService, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListPersistenceModeDefault, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewCardFieldViewModel, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListCardSelectActionName, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, AXPFileListComponent, AXPFileUploaderLoadFilesQuery, AXPFileUploaderSaveFilesCommand, AXPFileUploaderWidget, AXPFileUploaderWidgetColumnComponent, AXPFileUploaderWidgetEditComponent, AXPFileUploaderWidgetService, AXPFileUploaderWidgetViewComponent, AXPGetEntityDetailsQuery, AXPLayoutOrderingConfigService, AXPLookupWidget, AXPLookupWidgetColumnComponent, AXPLookupWidgetEditComponent, AXPLookupWidgetViewComponent, AXPMiddlewareAbortError, AXPMiddlewareEntityStorageService, AXPModifyEntitySectionWorkflow, AXPMultiSourceDefinitionProviderContext, AXPMultiSourceDefinitionProviderService, AXPMultiSourceFederatedSearchService, AXPMultiSourceSelectorComponent, AXPMultiSourceSelectorService, AXPMultiSourceSelectorWidget, AXPMultiSourceSelectorWidgetColumnComponent, AXPMultiSourceSelectorWidgetEditComponent, AXPMultiSourceSelectorWidgetViewComponent, AXPMultiSourceType, AXPOpenEntityDetailsCommand, AXPQuickEntityModifyPopupAction, AXPQuickModifyEntityWorkflow, AXPRelatedColumnEnrichmentService, AXPRelatedColumnMetadataResolver, AXPSelectorStructureWidget, AXPSelectorStructureWidgetColumnComponent, AXPSelectorStructureWidgetEditComponent, AXPSelectorStructureWidgetViewComponent, AXPShowDetailViewAction, AXPShowDetailsViewWorkflow, AXPShowListViewAction, AXPShowListViewWorkflow, AXPTruncatedBreadcrumbComponent, AXPUpdateEntityCommand, AXPViewEntityDetailsCommand, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXP_DATA_SEEDER_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER, AXP_ENTITY_STORAGE_BACKEND, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_MULTI_SOURCE_DEFINITION_PROVIDER, AXP_RECORD_WORKFLOW_INFO_CORRELATION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_DEFINITION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_INSTANCE_ID_FIELD, DEFAULT_COLUMN_ORDER, DEFAULT_PAIR_SPAN_RULES, DEFAULT_PROPERTY_ORDER, DEFAULT_SECTION_ORDER, ENTITY_LIST_ROUTE_CONTEXT_SESSION_KEY, EntityBuilder, EntityDataAccessor, actionExists, applyDataSourcePagingWithoutLoad, attachmentFieldCount, attachmentsPlugin, attachmentsSemanticallyEqual, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, buildAXPRecordWorkflowInfo, canPersistEntityListState, cloneLayoutArrays, collectEntityQuickSearchFieldPaths, collectNestedCreateHiddenProperties, collectNestedFieldPathsFromEntityColumns, collectQuickSearchPathsFromSingleEntityDefinition, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, committedAttachments, computeEntityAggregates, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, defaultCardLayoutMiddleware, defaultCardLayoutMiddlewareProvider, defaultMultiLanguageMiddleware, defaultMultiLanguageMiddlewareProvider, detectEntityChanges, ensureLayoutPropertyView, ensureLayoutSection, ensureListActions, entityDetailsCreateActions, entityDetailsCreateActionsDeferredParent, entityDetailsCrudActions, entityDetailsEditAction, entityDetailsNewEditAction, entityDetailsReferenceCondition, entityDetailsReferenceCreateActions, entityDetailsSimpleCondition, entityMasterBulkDeleteAction, entityMasterCreateAction, entityMasterCrudActions, entityMasterDeleteAction, entityMasterEditAction, entityMasterRecordActions, entityMasterViewAction, entityOverrideDetailsViewAction, eventDispatchMiddleware, filterSortEntityRows, findEntityListRowDataInTree, fingerprintAttachmentItem, fingerprintAttachments, formatLookupItemDisplay, getDataSourcePageIndex, getEntityListRowId, getMasterInterfacePropertySortKey, getRecordWorkflowCorrelationId, getRecordWorkflowInstanceId, hasFileUploaderTitleOrDescriptionFields, isAXPMiddlewareAbortError, isAttachmentListEntry, isCategoryEntity, isCategoryFilter, isFileListItem, isFileUploaderEditDialogAuto, isLegacyEntityDataSelectorOptions, isUnresolvedLookupDisplayTemplate, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mapLegacyEntityDataSelectorOptions, mergeForeignKeyFieldIntoCreateActions, normalizeEntityDataSelectorOptions, normalizeEntityFieldToFileList, normalizeEntityListPersistenceMode, normalizeListPaging, normalizeLookupDisplayTemplate, persistedAttachments, provideEntity, resolveEntityPluginDetailPageOrder, resolveFileUploaderEditDialog, resolveFileUploaderEntityScope, resolveLookupDisplayField, resolveLookupDisplayTemplate, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
|
|
21804
24842
|
//# sourceMappingURL=acorex-platform-layout-entity.mjs.map
|