@openmfp/webcomponents 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/.github/workflows/pipeline.yaml +41 -0
  2. package/.storybook/main.ts +34 -0
  3. package/.storybook/preview.ts +29 -0
  4. package/.storybook/tsconfig.json +11 -0
  5. package/AGENTS.md +153 -0
  6. package/CODEOWNERS +6 -0
  7. package/CONTRIBUTING.md +95 -0
  8. package/LICENSE +201 -0
  9. package/LICENSES/Apache-2.0.txt +73 -0
  10. package/README.md +91 -0
  11. package/REUSE.toml +9 -0
  12. package/angular.json +157 -0
  13. package/docs/dashboard.md +358 -0
  14. package/docs/declarative-form.md +178 -0
  15. package/docs/declarative-table-card.md +235 -0
  16. package/docs/declarative-table.md +315 -0
  17. package/eslint.config.js +41 -0
  18. package/package.json +73 -0
  19. package/projects/ngx/cards/favorites/favorites.component.html +12 -0
  20. package/projects/ngx/cards/favorites/favorites.component.scss +50 -0
  21. package/projects/ngx/cards/favorites/favorites.component.ts +19 -0
  22. package/projects/ngx/cards/public-api.ts +4 -0
  23. package/projects/ngx/cards/service-status/service-status-card.component.html +15 -0
  24. package/projects/ngx/cards/service-status/service-status-card.component.scss +87 -0
  25. package/projects/ngx/cards/service-status/service-status-card.component.ts +36 -0
  26. package/projects/ngx/cards/stories/visited-service-card.stories.ts +149 -0
  27. package/projects/ngx/cards/visited-service-card/visited-service-card.component.html +17 -0
  28. package/projects/ngx/cards/visited-service-card/visited-service-card.component.scss +34 -0
  29. package/projects/ngx/cards/visited-service-card/visited-service-card.component.ts +22 -0
  30. package/projects/ngx/cards/whats-new/whats-new.component.html +10 -0
  31. package/projects/ngx/cards/whats-new/whats-new.component.scss +25 -0
  32. package/projects/ngx/cards/whats-new/whats-new.component.ts +46 -0
  33. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.html +28 -0
  34. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.scss +44 -0
  35. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.spec.ts +85 -0
  36. package/projects/ngx/declarative-ui/dashboard/add-card-dialog/add-card-dialog.component.ts +58 -0
  37. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.html +29 -0
  38. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.scss +63 -0
  39. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.spec.ts +255 -0
  40. package/projects/ngx/declarative-ui/dashboard/card/dashboard-card.component.ts +75 -0
  41. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.spec.ts +76 -0
  42. package/projects/ngx/declarative-ui/dashboard/card/utils/dashboard-card-registry.ts +109 -0
  43. package/projects/ngx/declarative-ui/dashboard/card/utils/index.ts +4 -0
  44. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.spec.ts +141 -0
  45. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-angular-card.ts +44 -0
  46. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.spec.ts +142 -0
  47. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-sap-card.ts +52 -0
  48. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.spec.ts +107 -0
  49. package/projects/ngx/declarative-ui/dashboard/card/utils/mount-wc-card.ts +22 -0
  50. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.html +134 -0
  51. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.scss +88 -0
  52. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.spec.ts +354 -0
  53. package/projects/ngx/declarative-ui/dashboard/dashboard/dashboard.component.ts +238 -0
  54. package/projects/ngx/declarative-ui/dashboard/dashboard/index.ts +1 -0
  55. package/projects/ngx/declarative-ui/dashboard/index.ts +5 -0
  56. package/projects/ngx/declarative-ui/dashboard/models/constants.ts +2 -0
  57. package/projects/ngx/declarative-ui/dashboard/models/dashboard.model.ts +50 -0
  58. package/projects/ngx/declarative-ui/dashboard/models/index.ts +1 -0
  59. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.html +28 -0
  60. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.scss +85 -0
  61. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.spec.ts +104 -0
  62. package/projects/ngx/declarative-ui/dashboard/section/dashboard-section.component.ts +23 -0
  63. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.html +62 -0
  64. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.scss +12 -0
  65. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.spec.ts +301 -0
  66. package/projects/ngx/declarative-ui/form/declarative-form/declarative-form.component.ts +166 -0
  67. package/projects/ngx/declarative-ui/form/declarative-form/index.ts +1 -0
  68. package/projects/ngx/declarative-ui/form/index.ts +2 -0
  69. package/projects/ngx/declarative-ui/form/models/form-field-definition.ts +15 -0
  70. package/projects/ngx/declarative-ui/form/models/index.ts +1 -0
  71. package/projects/ngx/declarative-ui/form/utils/set-property-by-path.ts +30 -0
  72. package/projects/ngx/declarative-ui/models/index.ts +2 -0
  73. package/projects/ngx/declarative-ui/models/resource.ts +5 -0
  74. package/projects/ngx/declarative-ui/models/ui-definition.ts +95 -0
  75. package/projects/ngx/declarative-ui/public-api.ts +4 -0
  76. package/projects/ngx/declarative-ui/stories/add-card-dialog.stories.ts +91 -0
  77. package/projects/ngx/declarative-ui/stories/background-lightblue.png +0 -0
  78. package/projects/ngx/declarative-ui/stories/dashboard.cards.ts +107 -0
  79. package/projects/ngx/declarative-ui/stories/dashboard.stories.ts +296 -0
  80. package/projects/ngx/declarative-ui/stories/declarative-form.stories.ts +149 -0
  81. package/projects/ngx/declarative-ui/stories/declarative-table-card.stories.ts +358 -0
  82. package/projects/ngx/declarative-ui/stories/declarative-table.stories.ts +363 -0
  83. package/projects/ngx/declarative-ui/stories/pods-table.config.ts +188 -0
  84. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.html +138 -0
  85. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.scss +21 -0
  86. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.spec.ts +345 -0
  87. package/projects/ngx/declarative-ui/table/declarative-table/declarative-table.component.ts +61 -0
  88. package/projects/ngx/declarative-ui/table/declarative-table/index.ts +1 -0
  89. package/projects/ngx/declarative-ui/table/index.ts +2 -0
  90. package/projects/ngx/declarative-ui/table/models/index.ts +14 -0
  91. package/projects/ngx/declarative-ui/table/models/table.model.ts +17 -0
  92. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.spec.ts +146 -0
  93. package/projects/ngx/declarative-ui/table/utils/cssRules.engine.ts +69 -0
  94. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.spec.ts +70 -0
  95. package/projects/ngx/declarative-ui/table/utils/field-definition.utils.ts +13 -0
  96. package/projects/ngx/declarative-ui/table/utils/proccess-fields.spec.ts +511 -0
  97. package/projects/ngx/declarative-ui/table/utils/proccess-fields.ts +71 -0
  98. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.spec.ts +372 -0
  99. package/projects/ngx/declarative-ui/table/utils/resource-field-by-path.ts +98 -0
  100. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-cell.constants.ts +5 -0
  101. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.html +1 -0
  102. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.scss +0 -0
  103. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.spec.ts +119 -0
  104. package/projects/ngx/declarative-ui/table/value-cell/boolean-value/boolean-value.component.ts +35 -0
  105. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.html +7 -0
  106. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.scss +0 -0
  107. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.spec.ts +114 -0
  108. package/projects/ngx/declarative-ui/table/value-cell/link-value/link-value.component.ts +19 -0
  109. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.html +7 -0
  110. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.scss +10 -0
  111. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.spec.ts +188 -0
  112. package/projects/ngx/declarative-ui/table/value-cell/secret-value/secret-value.component.ts +16 -0
  113. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.html +59 -0
  114. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.scss +33 -0
  115. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.spec.ts +316 -0
  116. package/projects/ngx/declarative-ui/table/value-cell/value-cell.component.ts +115 -0
  117. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.html +156 -0
  118. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.scss +123 -0
  119. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.spec.ts +786 -0
  120. package/projects/ngx/declarative-ui/table-card/declarative-table-card.component.ts +286 -0
  121. package/projects/ngx/declarative-ui/table-card/index.ts +2 -0
  122. package/projects/ngx/declarative-ui/table-card/models/configs.ts +46 -0
  123. package/projects/ngx/declarative-ui/tsconfig.lib.json +11 -0
  124. package/projects/ngx/declarative-ui/tsconfig.lib.prod.json +9 -0
  125. package/projects/ngx/declarative-ui/tsconfig.spec.json +9 -0
  126. package/projects/ngx/ng-package.json +8 -0
  127. package/projects/ngx/package.json +22 -0
  128. package/projects/ngx/public-api.ts +2 -0
  129. package/projects/ngx/tsconfig.lib.json +11 -0
  130. package/projects/ngx/tsconfig.lib.prod.json +9 -0
  131. package/projects/webcomponents/main.ts +92 -0
  132. package/projects/webcomponents/tsconfig.app.json +9 -0
  133. package/projects/webcomponents-dashboard/main.ts +15 -0
  134. package/projects/webcomponents-dashboard/tsconfig.app.json +9 -0
  135. package/renovate.json +6 -0
  136. package/scripts/bundle-wc.mjs +79 -0
  137. package/tsconfig.json +37 -0
  138. package/tsconfig.spec.json +8 -0
  139. package/tsconfig.storybook.json +16 -0
  140. package/vitest.config.ts +26 -0
@@ -0,0 +1,286 @@
1
+ import { FormFieldChangeEvent, FormFieldDefinition } from '../form';
2
+ import { DeclarativeForm } from '../form/declarative-form/declarative-form.component';
3
+ import { GenericResource } from '../models';
4
+ import { DeclarativeTable } from '../table/declarative-table/declarative-table.component';
5
+ import {
6
+ TableFieldDefinition,
7
+ ValueCellButtonClickEvent,
8
+ } from '../table/models';
9
+ import { getResourceValueByJsonPath } from '../table/utils/resource-field-by-path';
10
+ import { TableCardConfig, TableCardFormState } from './models/configs';
11
+ import {
12
+ CUSTOM_ELEMENTS_SCHEMA,
13
+ ChangeDetectionStrategy,
14
+ Component,
15
+ Injector,
16
+ ViewEncapsulation,
17
+ afterNextRender,
18
+ computed,
19
+ inject,
20
+ input,
21
+ output,
22
+ signal,
23
+ viewChild,
24
+ } from '@angular/core';
25
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
26
+ import { FormControl, ReactiveFormsModule } from '@angular/forms';
27
+ import { Button } from '@fundamental-ngx/ui5-webcomponents/button';
28
+ import { Dialog } from '@fundamental-ngx/ui5-webcomponents/dialog';
29
+ import { Icon } from '@fundamental-ngx/ui5-webcomponents/icon';
30
+ import { Input } from '@fundamental-ngx/ui5-webcomponents/input';
31
+ import { Title } from '@fundamental-ngx/ui5-webcomponents/title';
32
+ import '@ui5/webcomponents-icons/dist/add.js';
33
+ import '@ui5/webcomponents-icons/dist/search.js';
34
+ import { debounceTime } from 'rxjs';
35
+
36
+ type SearchState = 'collapsed' | 'expanded' | 'collapsing';
37
+
38
+ @Component({
39
+ selector: 'mfp-declarative-table-card',
40
+ imports: [
41
+ DeclarativeTable,
42
+ DeclarativeForm,
43
+ ReactiveFormsModule,
44
+ Dialog,
45
+ Title,
46
+ Button,
47
+ Icon,
48
+ Input,
49
+ ],
50
+ templateUrl: './declarative-table-card.component.html',
51
+ styleUrl: './declarative-table-card.component.scss',
52
+ changeDetection: ChangeDetectionStrategy.OnPush,
53
+ encapsulation: ViewEncapsulation.ShadowDom,
54
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
55
+ })
56
+ export class DeclarativeTableCard<T extends GenericResource> {
57
+ resources = input.required<T[]>();
58
+
59
+ config = input.required<TableCardConfig>();
60
+ createFormState = input<TableCardFormState>({});
61
+ editFormState = input<TableCardFormState>({});
62
+
63
+ readonly actionButtonClick = output<ValueCellButtonClickEvent<T>>();
64
+ readonly tableRowClicked = output<T>();
65
+ readonly loadMoreResources = output<void>();
66
+ readonly paginationLimitChanged = output<number>();
67
+
68
+ readonly searchChanged = output<string>();
69
+ readonly createFieldChange = output<FormFieldChangeEvent>();
70
+ readonly editFieldChange = output<{
71
+ resource: T;
72
+ formChangeEvent: FormFieldChangeEvent;
73
+ }>();
74
+ readonly createSubmit = output<Record<string, unknown>>();
75
+ readonly editSubmit = output<{
76
+ resource: T;
77
+ value: Record<string, unknown>;
78
+ }>();
79
+ readonly deleteSubmit = output<T>();
80
+
81
+ protected searchState = signal<SearchState>('collapsed');
82
+ protected searchExpanded = computed(() => this.searchState() !== 'collapsed');
83
+ protected searchCollapsing = computed(
84
+ () => this.searchState() === 'collapsing',
85
+ );
86
+ protected searchControl = new FormControl('');
87
+ protected searchInputRef = viewChild<Input>('searchInput');
88
+
89
+ protected createDialogOpen = signal(false);
90
+ protected editDialogOpen = signal(false);
91
+ protected deleteDialogOpen = signal(false);
92
+
93
+ protected pendingResource = signal<T | null>(null);
94
+
95
+ protected tableConfig = computed(() => this.config().tableConfig);
96
+ protected header = computed(() => this.config().header);
97
+ protected headerTooltip = computed(() => this.config().headerTooltip);
98
+ protected createFormConfig = computed(
99
+ () => this.config().createResourceFormConfig,
100
+ );
101
+ protected editFormConfig = computed(
102
+ () => this.config().editResourceFormConfig,
103
+ );
104
+ protected deleteConfirmationConfig = computed(
105
+ () => this.config().deleteResourceConfirmationConfig,
106
+ );
107
+ protected createButtonConfig = computed(
108
+ () => this.config().buttonSettings?.createButton,
109
+ );
110
+ protected searchButtonConfig = computed(
111
+ () => this.config().buttonSettings?.searchButton,
112
+ );
113
+ protected effectiveColumns = computed(() => this.addActionsColumn());
114
+ protected editInitialValue = computed(() => {
115
+ const pendingResource = this.pendingResource();
116
+ const editConfig = this.editFormConfig();
117
+ if (!pendingResource || !editConfig) {
118
+ return {};
119
+ }
120
+
121
+ return this.buildInitialValues(editConfig.fields, pendingResource);
122
+ });
123
+
124
+ private readonly injector = inject(Injector);
125
+
126
+ constructor() {
127
+ this.searchControl.valueChanges
128
+ .pipe(debounceTime(300), takeUntilDestroyed())
129
+ .subscribe((value) => {
130
+ this.searchChanged.emit(value ?? '');
131
+ });
132
+ }
133
+
134
+ toggleSearch(): void {
135
+ if (this.searchState() === 'expanded') {
136
+ this.collapseSearch();
137
+ } else if (this.searchState() === 'collapsed') {
138
+ this.searchState.set('expanded');
139
+ afterNextRender(
140
+ () => {
141
+ this.searchInputRef()?.elementRef.nativeElement.focus();
142
+ },
143
+ { injector: this.injector },
144
+ );
145
+ }
146
+ }
147
+
148
+ onSearchBlur(): void {
149
+ if (!this.searchControl.value) {
150
+ this.collapseSearch();
151
+ }
152
+ }
153
+
154
+ onSearchAnimationEnd(): void {
155
+ if (this.searchCollapsing()) {
156
+ this.searchState.set('collapsed');
157
+ this.searchControl.setValue('', { emitEvent: false });
158
+ }
159
+ }
160
+
161
+ private collapseSearch(): void {
162
+ this.searchState.set('collapsing');
163
+ }
164
+
165
+ onButtonClick(event: ValueCellButtonClickEvent<T>): void {
166
+ const action = event.field.uiSettings?.buttonSettings?.action;
167
+ if (action === 'edit' && event.resource) {
168
+ this.pendingResource.set(event.resource);
169
+ this.editDialogOpen.set(true);
170
+ return;
171
+ }
172
+ if (action === 'delete' && event.resource) {
173
+ this.pendingResource.set(event.resource);
174
+ this.deleteDialogOpen.set(true);
175
+ return;
176
+ }
177
+
178
+ this.actionButtonClick.emit(event);
179
+ }
180
+
181
+ closeCreateDialog(): void {
182
+ this.createDialogOpen.set(false);
183
+ }
184
+
185
+ closeEditDialog(): void {
186
+ this.editDialogOpen.set(false);
187
+ }
188
+
189
+ closeDeleteDialog(): void {
190
+ this.deleteDialogOpen.set(false);
191
+ }
192
+
193
+ onCreateFieldChange(event: FormFieldChangeEvent): void {
194
+ this.createFieldChange.emit(event);
195
+ }
196
+
197
+ onEditFieldChange(event: FormFieldChangeEvent): void {
198
+ const resource = this.pendingResource();
199
+
200
+ if (resource) {
201
+ this.editFieldChange.emit({ resource, formChangeEvent: event });
202
+ }
203
+ }
204
+
205
+ onCreateSubmit(value: Record<string, unknown>): void {
206
+ this.createSubmit.emit(value);
207
+ }
208
+
209
+ onEditSubmit(value: Record<string, unknown>): void {
210
+ const resource = this.pendingResource();
211
+
212
+ if (resource) {
213
+ this.editSubmit.emit({ resource, value });
214
+ }
215
+ }
216
+
217
+ onDeleteSubmit(): void {
218
+ const resource = this.pendingResource();
219
+ if (resource) this.deleteSubmit.emit(resource);
220
+ }
221
+
222
+ protected hasErrors(state: TableCardFormState): boolean {
223
+ const errors = state.fieldErrors;
224
+ return !!errors && Object.values(errors).some((val) => !!val);
225
+ }
226
+
227
+ private addActionsColumn(): TableFieldDefinition[] {
228
+ const cols = this.tableConfig().fields;
229
+ const buttonSettings = this.config().buttonSettings;
230
+ const editButton = this.editFormConfig();
231
+ const deleteButton = this.deleteConfirmationConfig();
232
+ const actions: TableFieldDefinition[] = [];
233
+
234
+ if (editButton) {
235
+ actions.push({
236
+ uiSettings: {
237
+ displayAs: 'button',
238
+ buttonSettings: {
239
+ icon: 'edit',
240
+ design: 'Transparent',
241
+ action: 'edit',
242
+ ...buttonSettings?.editButton,
243
+ },
244
+ },
245
+ group: { name: 'actions', label: '', multiline: false },
246
+ });
247
+ }
248
+
249
+ if (deleteButton) {
250
+ actions.push({
251
+ uiSettings: {
252
+ displayAs: 'button',
253
+ buttonSettings: {
254
+ icon: 'decline',
255
+ design: 'Transparent',
256
+ action: 'delete',
257
+ ...buttonSettings?.deleteButton,
258
+ },
259
+ },
260
+ group: { name: 'actions', label: '', multiline: false },
261
+ });
262
+ }
263
+
264
+ return cols.concat(actions);
265
+ }
266
+
267
+ private buildInitialValues(
268
+ fields: FormFieldDefinition[],
269
+ resource?: T,
270
+ ): Record<string, unknown> {
271
+ if (!resource) return {};
272
+
273
+ return fields.reduce(
274
+ (acc, field) => {
275
+ if (typeof field.name === 'string') {
276
+ acc[field.name] =
277
+ getResourceValueByJsonPath(resource, { property: field.name }) ??
278
+ '';
279
+ }
280
+ return acc;
281
+ },
282
+ {} as Record<string, unknown>,
283
+ );
284
+ }
285
+
286
+ }
@@ -0,0 +1,2 @@
1
+ export * from './declarative-table-card.component';
2
+ export * from './models/configs';
@@ -0,0 +1,46 @@
1
+ import { FormFieldDefinition, FormFieldErrors } from '../../form';
2
+ import { ButtonSettings } from '../../models';
3
+ import { TableFieldDefinition } from '../../table';
4
+
5
+ export interface ResourceFormConfig {
6
+ fields: FormFieldDefinition[];
7
+ title?: string;
8
+ confirmLabel?: string;
9
+ cancelLabel?: string;
10
+ }
11
+
12
+ export interface TableCardFormState {
13
+ fieldErrors?: FormFieldErrors;
14
+ }
15
+
16
+ export interface DeleteResourceConfirmationConfig {
17
+ title?: string;
18
+ message?: string;
19
+ confirmLabel?: string;
20
+ cancelLabel?: string;
21
+ }
22
+
23
+ export interface TableConfig {
24
+ fields: TableFieldDefinition[];
25
+ totalItemsCount?: number;
26
+ paginationLimit?: number;
27
+ hasMore?: boolean;
28
+ }
29
+
30
+ export interface TableCardButtonSettings {
31
+ createButton?: Partial<ButtonSettings>;
32
+ searchButton?: Partial<ButtonSettings>;
33
+ editButton?: Partial<ButtonSettings>;
34
+ deleteButton?: Partial<ButtonSettings>;
35
+ }
36
+
37
+ export interface TableCardConfig {
38
+ header?: string;
39
+ headerTooltip?: string;
40
+ tableConfig: TableConfig;
41
+ buttonSettings?: TableCardButtonSettings;
42
+ resourcesSearchable?: boolean;
43
+ createResourceFormConfig?: ResourceFormConfig;
44
+ editResourceFormConfig?: ResourceFormConfig;
45
+ deleteResourceConfirmationConfig?: DeleteResourceConfirmationConfig;
46
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../out-tsc/lib",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "types": []
8
+ },
9
+ "include": ["**/*.ts"],
10
+ "exclude": ["**/*.spec.ts"]
11
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "declarationMap": false
5
+ },
6
+ "angularCompilerOptions": {
7
+ "compilationMode": "partial"
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../out-tsc/spec",
5
+ "types": ["vitest/globals"],
6
+ "esModuleInterop": true
7
+ },
8
+ "include": ["**/*.d.ts", "**/*.spec.ts"]
9
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/ngx",
4
+ "lib": {
5
+ "entryFile": "public-api.ts"
6
+ },
7
+ "allowedNonPeerDependencies": ["tslib", "gridstack"]
8
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@openmfp/ngx",
3
+ "version": "0.1.0",
4
+ "dependencies": {
5
+ "gridstack": "^12.6.0",
6
+ "tslib": "2.8.1"
7
+ },
8
+ "peerDependencies": {
9
+ "@angular/animations": "^21.2.1",
10
+ "@angular/common": "^21.2.1",
11
+ "@angular/compiler": "^21.2.1",
12
+ "@angular/core": "^21.2.1",
13
+ "@angular/elements": "^21.2.1",
14
+ "@angular/forms": "^21.2.1",
15
+ "@angular/platform-browser": "^21.2.1",
16
+ "@fundamental-ngx/ui5-webcomponents": "^0.59.1",
17
+ "@fundamental-ngx/ui5-webcomponents-fiori": "^0.59.1",
18
+ "jsonpath": "^1.1.1",
19
+ "rxjs": "^7.8.2"
20
+ },
21
+ "type": "module"
22
+ }
@@ -0,0 +1,2 @@
1
+ export * from './declarative-ui/public-api';
2
+ export * from './cards/public-api';
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/lib",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "types": []
8
+ },
9
+ "include": ["**/*.ts"],
10
+ "exclude": ["**/*.spec.ts"]
11
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "declarationMap": false
5
+ },
6
+ "angularCompilerOptions": {
7
+ "compilationMode": "partial"
8
+ }
9
+ }
@@ -0,0 +1,92 @@
1
+ import { VisitedServiceCard } from '../ngx/cards/visited-service-card/visited-service-card.component';
2
+ import { createCustomElement } from '@angular/elements';
3
+ import { createApplication } from '@angular/platform-browser';
4
+ import {
5
+ DeclarativeForm,
6
+ DeclarativeTable,
7
+ DeclarativeTableCard,
8
+ } from '@openmfp/webcomponents/declarative-ui';
9
+ import { ignoreCustomElements } from '@ui5/webcomponents-base/dist/IgnoreCustomElements.js';
10
+
11
+ ignoreCustomElements('mfp');
12
+
13
+ (async () => {
14
+ const app = await createApplication();
15
+
16
+ const DeclarativeTableElement = createCustomElement(DeclarativeTable, {
17
+ injector: app.injector,
18
+ });
19
+ customElements.define('mfp-wc-declarative-table', DeclarativeTableElement);
20
+
21
+ const DeclarativeFormElementBase = createCustomElement(DeclarativeForm, {
22
+ injector: app.injector,
23
+ }) as CustomElementConstructor;
24
+ class DeclarativeFormElement extends DeclarativeFormElementBase {
25
+ submit(): void {
26
+ const strategy = (
27
+ this as unknown as {
28
+ ngElementStrategy: {
29
+ componentRef?: { instance: DeclarativeForm };
30
+ };
31
+ }
32
+ ).ngElementStrategy;
33
+ strategy.componentRef?.instance.submit();
34
+ }
35
+
36
+ clear(): void {
37
+ const strategy = (
38
+ this as unknown as {
39
+ ngElementStrategy: {
40
+ componentRef?: { instance: DeclarativeForm };
41
+ };
42
+ }
43
+ ).ngElementStrategy;
44
+ strategy.componentRef?.instance.clear();
45
+ }
46
+ }
47
+ customElements.define('mfp-wc-declarative-form', DeclarativeFormElement);
48
+
49
+ const DeclarativeTableCardElementBase = createCustomElement(
50
+ DeclarativeTableCard,
51
+ {
52
+ injector: app.injector,
53
+ },
54
+ ) as CustomElementConstructor;
55
+ class DeclarativeTableCardElement extends DeclarativeTableCardElementBase {
56
+ closeCreateDialog(): void {
57
+ this.componentInstance()?.closeCreateDialog();
58
+ }
59
+
60
+ closeEditDialog(): void {
61
+ this.componentInstance()?.closeEditDialog();
62
+ }
63
+
64
+ closeDeleteDialog(): void {
65
+ this.componentInstance()?.closeDeleteDialog();
66
+ }
67
+
68
+ private componentInstance(): DeclarativeTableCard<never> | undefined {
69
+ const strategy = (
70
+ this as unknown as {
71
+ ngElementStrategy: {
72
+ componentRef?: { instance: DeclarativeTableCard<never> };
73
+ };
74
+ }
75
+ ).ngElementStrategy;
76
+
77
+ return strategy.componentRef?.instance;
78
+ }
79
+ }
80
+ customElements.define(
81
+ 'mfp-wc-declarative-table-card',
82
+ DeclarativeTableCardElement,
83
+ );
84
+
85
+ const VisitedServiceCardElement = createCustomElement(VisitedServiceCard, {
86
+ injector: app.injector,
87
+ });
88
+ customElements.define(
89
+ 'mfp-wc-visited-service-card',
90
+ VisitedServiceCardElement,
91
+ );
92
+ })();
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/app",
5
+ "types": []
6
+ },
7
+ "files": ["main.ts"],
8
+ "exclude": ["**/*.spec.ts"]
9
+ }
@@ -0,0 +1,15 @@
1
+ import { createCustomElement } from '@angular/elements';
2
+ import { createApplication } from '@angular/platform-browser';
3
+ import { Dashboard } from '@openmfp/webcomponents/declarative-ui';
4
+ import { ignoreCustomElements } from '@ui5/webcomponents-base/dist/IgnoreCustomElements.js';
5
+
6
+ ignoreCustomElements('mfp');
7
+
8
+ (async () => {
9
+ const app = await createApplication();
10
+
11
+ const DashboardElement = createCustomElement(Dashboard, {
12
+ injector: app.injector,
13
+ });
14
+ customElements.define('mfp-wc-dashboard', DashboardElement);
15
+ })();
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../out-tsc/app",
5
+ "types": []
6
+ },
7
+ "files": ["main.ts"],
8
+ "exclude": ["**/*.spec.ts"]
9
+ }
package/renovate.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "local>openmfp/.github:renovate-config"
5
+ ]
6
+ }
@@ -0,0 +1,79 @@
1
+ import { build } from 'esbuild';
2
+ import { readdirSync, unlinkSync, rmSync, mkdirSync, copyFileSync, writeFileSync, readFileSync } from 'fs';
3
+ import { join, resolve } from 'path';
4
+
5
+ const publicDir = resolve('public');
6
+ mkdirSync(publicDir, { recursive: true });
7
+
8
+ const cleanup = ['prerendered-routes.json', '3rdpartylicenses.txt'];
9
+
10
+ function cleanDist(dir) {
11
+ for (const file of readdirSync(dir)) {
12
+ if (
13
+ (file.startsWith('chunk-') && file.endsWith('.js')) ||
14
+ cleanup.includes(file)
15
+ ) {
16
+ unlinkSync(join(dir, file));
17
+ }
18
+ }
19
+ }
20
+
21
+ // --- mfp-webcomponents.js (all components) ---
22
+ const dist = resolve('dist/webcomponents');
23
+ const entry = join(dist, 'main.js');
24
+ const out = join(dist, 'mfp-webcomponents.js');
25
+
26
+ await build({
27
+ entryPoints: [entry],
28
+ bundle: true,
29
+ format: 'esm',
30
+ outfile: out,
31
+ minify: true,
32
+ logLevel: 'warning',
33
+ });
34
+
35
+ unlinkSync(entry);
36
+ cleanDist(dist);
37
+
38
+ console.log('Single-file bundle written to dist/webcomponents/mfp-webcomponents.js');
39
+ copyFileSync(out, join(publicDir, 'mfp-webcomponents.js'));
40
+ console.log('Copied to public/mfp-webcomponents.js');
41
+
42
+ // --- mfp-wc-dashboard.js (dashboard only) ---
43
+ const dashDist = resolve('dist/webcomponents-dashboard');
44
+ const dashEntry = join(dashDist, 'main.js');
45
+ const dashOut = join(dist, 'mfp-wc-dashboard.js');
46
+
47
+ await build({
48
+ entryPoints: [dashEntry],
49
+ bundle: true,
50
+ format: 'esm',
51
+ outfile: dashOut,
52
+ minify: true,
53
+ logLevel: 'warning',
54
+ });
55
+
56
+ unlinkSync(dashEntry);
57
+ cleanDist(dashDist);
58
+ rmSync(dashDist, { recursive: true });
59
+
60
+ console.log('Single-file bundle written to dist/webcomponents/mfp-wc-dashboard.js');
61
+ copyFileSync(dashOut, join(publicDir, 'mfp-wc-dashboard.js'));
62
+ console.log('Copied to public/mfp-wc-dashboard.js');
63
+
64
+ // --- generate package.json for dist/webcomponents ---
65
+ const rootPkg = JSON.parse(readFileSync(resolve('package.json'), 'utf8'));
66
+ const wcPkg = {
67
+ name: '@openmfp/webcomponents',
68
+ version: rootPkg.version,
69
+ description: rootPkg.description ?? 'OpenMFP web components bundle',
70
+ license: rootPkg.license ?? 'Apache-2.0',
71
+ type: 'module',
72
+ exports: {
73
+ '.': './mfp-webcomponents.js',
74
+ './dashboard': './mfp-wc-dashboard.js',
75
+ },
76
+ files: ['mfp-webcomponents.js', 'mfp-wc-dashboard.js'],
77
+ };
78
+ writeFileSync(join(dist, 'package.json'), JSON.stringify(wcPkg, null, 2) + '\n');
79
+ console.log('Generated dist/webcomponents/package.json');
package/tsconfig.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "compileOnSave": false,
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "noImplicitOverride": true,
6
+ "noPropertyAccessFromIndexSignature": true,
7
+ "noImplicitReturns": true,
8
+ "noFallthroughCasesInSwitch": true,
9
+ "skipLibCheck": true,
10
+ "isolatedModules": true,
11
+ "experimentalDecorators": true,
12
+ "importHelpers": true,
13
+ "target": "ES2022",
14
+ "module": "preserve",
15
+ "strictPropertyInitialization": false,
16
+ "paths": {
17
+ "@openmfp/webcomponents/declarative-ui": [
18
+ "./projects/ngx/declarative-ui/public-api.ts"
19
+ ]
20
+ }
21
+ },
22
+ "angularCompilerOptions": {
23
+ "strictInjectionParameters": true,
24
+ "strictInputAccessModifiers": true,
25
+ "strictTemplates": true
26
+ },
27
+ "files": [],
28
+ "exclude": ["vitest.config.ts"],
29
+ "references": [
30
+ {
31
+ "path": "./projects/ngx/declarative-ui/tsconfig.lib.json"
32
+ },
33
+ {
34
+ "path": "./projects/ngx/declarative-ui/tsconfig.spec.json"
35
+ }
36
+ ]
37
+ }