angular-data-mapper 1.0.0
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/.claude/settings.local.json +10 -0
- package/LICENSE +190 -0
- package/PUBLISHING.md +75 -0
- package/README.md +214 -0
- package/angular.json +121 -0
- package/package.json +67 -0
- package/projects/demo-app/public/favicon.ico +0 -0
- package/projects/demo-app/src/app/app.config.ts +12 -0
- package/projects/demo-app/src/app/app.html +36 -0
- package/projects/demo-app/src/app/app.routes.ts +62 -0
- package/projects/demo-app/src/app/app.scss +65 -0
- package/projects/demo-app/src/app/app.ts +11 -0
- package/projects/demo-app/src/app/layout/app-layout.component.ts +294 -0
- package/projects/demo-app/src/app/pages/mapper-page/mapper-page.component.html +87 -0
- package/projects/demo-app/src/app/pages/mapper-page/mapper-page.component.scss +202 -0
- package/projects/demo-app/src/app/pages/mapper-page/mapper-page.component.ts +192 -0
- package/projects/demo-app/src/app/pages/mappings-page/add-mapping-dialog.component.ts +163 -0
- package/projects/demo-app/src/app/pages/mappings-page/mappings-page.component.ts +306 -0
- package/projects/demo-app/src/app/pages/schema-creator-page/schema-creator-page.component.ts +88 -0
- package/projects/demo-app/src/app/pages/schema-editor-page/schema-editor-page.component.html +108 -0
- package/projects/demo-app/src/app/pages/schema-editor-page/schema-editor-page.component.scss +317 -0
- package/projects/demo-app/src/app/pages/schema-editor-page/schema-editor-page.component.ts +129 -0
- package/projects/demo-app/src/app/services/app-state.service.ts +233 -0
- package/projects/demo-app/src/app/services/sample-data.service.ts +228 -0
- package/projects/demo-app/src/index.html +15 -0
- package/projects/demo-app/src/main.ts +6 -0
- package/projects/demo-app/src/styles.scss +54 -0
- package/projects/demo-app/tsconfig.app.json +13 -0
- package/projects/ngx-data-mapper/ng-package.json +7 -0
- package/projects/ngx-data-mapper/package.json +40 -0
- package/projects/ngx-data-mapper/src/lib/components/array-filter-modal/array-filter-modal.component.html +183 -0
- package/projects/ngx-data-mapper/src/lib/components/array-filter-modal/array-filter-modal.component.scss +352 -0
- package/projects/ngx-data-mapper/src/lib/components/array-filter-modal/array-filter-modal.component.ts +277 -0
- package/projects/ngx-data-mapper/src/lib/components/array-selector-modal/array-selector-modal.component.html +174 -0
- package/projects/ngx-data-mapper/src/lib/components/array-selector-modal/array-selector-modal.component.scss +357 -0
- package/projects/ngx-data-mapper/src/lib/components/array-selector-modal/array-selector-modal.component.ts +258 -0
- package/projects/ngx-data-mapper/src/lib/components/condition-builder/condition-builder.component.html +139 -0
- package/projects/ngx-data-mapper/src/lib/components/condition-builder/condition-builder.component.scss +213 -0
- package/projects/ngx-data-mapper/src/lib/components/condition-builder/condition-builder.component.ts +261 -0
- package/projects/ngx-data-mapper/src/lib/components/data-mapper/data-mapper.component.html +199 -0
- package/projects/ngx-data-mapper/src/lib/components/data-mapper/data-mapper.component.scss +321 -0
- package/projects/ngx-data-mapper/src/lib/components/data-mapper/data-mapper.component.ts +618 -0
- package/projects/ngx-data-mapper/src/lib/components/default-value-popover/default-value-popover.component.html +67 -0
- package/projects/ngx-data-mapper/src/lib/components/default-value-popover/default-value-popover.component.scss +97 -0
- package/projects/ngx-data-mapper/src/lib/components/default-value-popover/default-value-popover.component.ts +105 -0
- package/projects/ngx-data-mapper/src/lib/components/schema-editor/schema-editor.component.html +552 -0
- package/projects/ngx-data-mapper/src/lib/components/schema-editor/schema-editor.component.scss +824 -0
- package/projects/ngx-data-mapper/src/lib/components/schema-editor/schema-editor.component.ts +730 -0
- package/projects/ngx-data-mapper/src/lib/components/schema-tree/schema-tree.component.html +82 -0
- package/projects/ngx-data-mapper/src/lib/components/schema-tree/schema-tree.component.scss +352 -0
- package/projects/ngx-data-mapper/src/lib/components/schema-tree/schema-tree.component.ts +225 -0
- package/projects/ngx-data-mapper/src/lib/components/transformation-popover/transformation-popover.component.html +346 -0
- package/projects/ngx-data-mapper/src/lib/components/transformation-popover/transformation-popover.component.scss +511 -0
- package/projects/ngx-data-mapper/src/lib/components/transformation-popover/transformation-popover.component.ts +368 -0
- package/projects/ngx-data-mapper/src/lib/models/json-schema.model.ts +164 -0
- package/projects/ngx-data-mapper/src/lib/models/schema.model.ts +173 -0
- package/projects/ngx-data-mapper/src/lib/services/mapping.service.ts +615 -0
- package/projects/ngx-data-mapper/src/lib/services/schema-parser.service.ts +270 -0
- package/projects/ngx-data-mapper/src/lib/services/svg-connector.service.ts +135 -0
- package/projects/ngx-data-mapper/src/lib/services/transformation.service.ts +453 -0
- package/projects/ngx-data-mapper/src/public-api.ts +22 -0
- package/projects/ngx-data-mapper/tsconfig.lib.json +13 -0
- package/projects/ngx-data-mapper/tsconfig.lib.prod.json +9 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
TransformationConfig,
|
|
4
|
+
TransformationType,
|
|
5
|
+
SchemaField,
|
|
6
|
+
FilterGroup,
|
|
7
|
+
FilterCondition,
|
|
8
|
+
FilterItem,
|
|
9
|
+
FilterOperator,
|
|
10
|
+
} from '../models/schema.model';
|
|
11
|
+
|
|
12
|
+
@Injectable({
|
|
13
|
+
providedIn: 'root',
|
|
14
|
+
})
|
|
15
|
+
export class TransformationService {
|
|
16
|
+
applyTransformation(
|
|
17
|
+
sourceValues: Record<string, unknown>,
|
|
18
|
+
sourceFields: SchemaField[],
|
|
19
|
+
config: TransformationConfig
|
|
20
|
+
): string {
|
|
21
|
+
const values = sourceFields.map((f) => this.getValueByPath(sourceValues, f.path));
|
|
22
|
+
|
|
23
|
+
switch (config.type) {
|
|
24
|
+
case 'direct':
|
|
25
|
+
return String(values[0] ?? '');
|
|
26
|
+
|
|
27
|
+
case 'concat':
|
|
28
|
+
if (config.template) {
|
|
29
|
+
return this.applyTemplate(config.template, sourceFields, sourceValues);
|
|
30
|
+
}
|
|
31
|
+
return values.join(config.separator ?? ' ');
|
|
32
|
+
|
|
33
|
+
case 'substring':
|
|
34
|
+
const str = String(values[0] ?? '');
|
|
35
|
+
return str.substring(
|
|
36
|
+
config.startIndex ?? 0,
|
|
37
|
+
config.endIndex ?? str.length
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
case 'replace':
|
|
41
|
+
return String(values[0] ?? '').replace(
|
|
42
|
+
new RegExp(config.searchValue ?? '', 'g'),
|
|
43
|
+
config.replaceValue ?? ''
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
case 'uppercase':
|
|
47
|
+
return String(values[0] ?? '').toUpperCase();
|
|
48
|
+
|
|
49
|
+
case 'lowercase':
|
|
50
|
+
return String(values[0] ?? '').toLowerCase();
|
|
51
|
+
|
|
52
|
+
case 'trim':
|
|
53
|
+
return String(values[0] ?? '').trim();
|
|
54
|
+
|
|
55
|
+
case 'mask':
|
|
56
|
+
return this.applyMask(String(values[0] ?? ''), config.pattern ?? '');
|
|
57
|
+
|
|
58
|
+
case 'dateFormat':
|
|
59
|
+
return this.formatDate(
|
|
60
|
+
values[0],
|
|
61
|
+
config.inputFormat,
|
|
62
|
+
config.outputFormat
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
case 'extractYear':
|
|
66
|
+
return this.extractDatePart(values[0], 'year');
|
|
67
|
+
|
|
68
|
+
case 'extractMonth':
|
|
69
|
+
return this.extractDatePart(values[0], 'month');
|
|
70
|
+
|
|
71
|
+
case 'extractDay':
|
|
72
|
+
return this.extractDatePart(values[0], 'day');
|
|
73
|
+
|
|
74
|
+
case 'extractHour':
|
|
75
|
+
return this.extractDatePart(values[0], 'hour');
|
|
76
|
+
|
|
77
|
+
case 'extractMinute':
|
|
78
|
+
return this.extractDatePart(values[0], 'minute');
|
|
79
|
+
|
|
80
|
+
case 'extractSecond':
|
|
81
|
+
return this.extractDatePart(values[0], 'second');
|
|
82
|
+
|
|
83
|
+
case 'numberFormat':
|
|
84
|
+
return this.formatNumber(
|
|
85
|
+
values[0],
|
|
86
|
+
config.decimalPlaces,
|
|
87
|
+
config.prefix,
|
|
88
|
+
config.suffix
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
case 'template':
|
|
92
|
+
return this.applyTemplate(
|
|
93
|
+
config.template ?? '',
|
|
94
|
+
sourceFields,
|
|
95
|
+
sourceValues
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
default:
|
|
99
|
+
return String(values[0] ?? '');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Apply a transformation to a single value (for chained transformations)
|
|
105
|
+
*/
|
|
106
|
+
applyTransformationToValue(
|
|
107
|
+
value: unknown,
|
|
108
|
+
config: TransformationConfig
|
|
109
|
+
): string {
|
|
110
|
+
const str = String(value ?? '');
|
|
111
|
+
|
|
112
|
+
switch (config.type) {
|
|
113
|
+
case 'direct':
|
|
114
|
+
return str;
|
|
115
|
+
|
|
116
|
+
case 'concat':
|
|
117
|
+
// For single value, just return it (concat needs multiple values)
|
|
118
|
+
return str;
|
|
119
|
+
|
|
120
|
+
case 'substring':
|
|
121
|
+
return str.substring(
|
|
122
|
+
config.startIndex ?? 0,
|
|
123
|
+
config.endIndex ?? str.length
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
case 'replace':
|
|
127
|
+
return str.replace(
|
|
128
|
+
new RegExp(config.searchValue ?? '', 'g'),
|
|
129
|
+
config.replaceValue ?? ''
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
case 'uppercase':
|
|
133
|
+
return str.toUpperCase();
|
|
134
|
+
|
|
135
|
+
case 'lowercase':
|
|
136
|
+
return str.toLowerCase();
|
|
137
|
+
|
|
138
|
+
case 'trim':
|
|
139
|
+
return str.trim();
|
|
140
|
+
|
|
141
|
+
case 'mask':
|
|
142
|
+
return this.applyMask(str, config.pattern ?? '');
|
|
143
|
+
|
|
144
|
+
case 'dateFormat':
|
|
145
|
+
return this.formatDate(value, config.inputFormat, config.outputFormat);
|
|
146
|
+
|
|
147
|
+
case 'extractYear':
|
|
148
|
+
return this.extractDatePart(value, 'year');
|
|
149
|
+
|
|
150
|
+
case 'extractMonth':
|
|
151
|
+
return this.extractDatePart(value, 'month');
|
|
152
|
+
|
|
153
|
+
case 'extractDay':
|
|
154
|
+
return this.extractDatePart(value, 'day');
|
|
155
|
+
|
|
156
|
+
case 'extractHour':
|
|
157
|
+
return this.extractDatePart(value, 'hour');
|
|
158
|
+
|
|
159
|
+
case 'extractMinute':
|
|
160
|
+
return this.extractDatePart(value, 'minute');
|
|
161
|
+
|
|
162
|
+
case 'extractSecond':
|
|
163
|
+
return this.extractDatePart(value, 'second');
|
|
164
|
+
|
|
165
|
+
case 'numberFormat':
|
|
166
|
+
return this.formatNumber(
|
|
167
|
+
value,
|
|
168
|
+
config.decimalPlaces,
|
|
169
|
+
config.prefix,
|
|
170
|
+
config.suffix
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
case 'template':
|
|
174
|
+
// For single value, replace {0} with the value
|
|
175
|
+
return (config.template ?? '').replace(/\{0\}/g, str);
|
|
176
|
+
|
|
177
|
+
default:
|
|
178
|
+
return str;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Apply multiple transformations in sequence, respecting conditions
|
|
184
|
+
*/
|
|
185
|
+
applyTransformations(
|
|
186
|
+
sourceValues: Record<string, unknown>,
|
|
187
|
+
sourceFields: SchemaField[],
|
|
188
|
+
transformations: TransformationConfig[]
|
|
189
|
+
): string {
|
|
190
|
+
if (transformations.length === 0) {
|
|
191
|
+
return '';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Get initial value for first transformation
|
|
195
|
+
const initialValues = sourceFields.map((f) => this.getValueByPath(sourceValues, f.path));
|
|
196
|
+
const initialValue = initialValues.length === 1 ? initialValues[0] : initialValues.join(' ');
|
|
197
|
+
|
|
198
|
+
// Apply first transformation using source fields (check condition first)
|
|
199
|
+
let result: string;
|
|
200
|
+
if (this.isConditionMet(initialValue, transformations[0])) {
|
|
201
|
+
result = this.applyTransformation(sourceValues, sourceFields, transformations[0]);
|
|
202
|
+
} else {
|
|
203
|
+
result = String(initialValue ?? '');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Apply subsequent transformations to the result
|
|
207
|
+
for (let i = 1; i < transformations.length; i++) {
|
|
208
|
+
if (this.isConditionMet(result, transformations[i])) {
|
|
209
|
+
result = this.applyTransformationToValue(result, transformations[i]);
|
|
210
|
+
}
|
|
211
|
+
// If condition not met, result passes through unchanged
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private getValueByPath(obj: Record<string, unknown>, path: string): unknown {
|
|
218
|
+
return path.split('.').reduce((acc: unknown, part) => {
|
|
219
|
+
if (acc && typeof acc === 'object') {
|
|
220
|
+
return (acc as Record<string, unknown>)[part];
|
|
221
|
+
}
|
|
222
|
+
return undefined;
|
|
223
|
+
}, obj);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private applyTemplate(
|
|
227
|
+
template: string,
|
|
228
|
+
sourceFields: SchemaField[],
|
|
229
|
+
sourceValues: Record<string, unknown>
|
|
230
|
+
): string {
|
|
231
|
+
let result = template;
|
|
232
|
+
// Replace positional placeholders {0}, {1}, etc.
|
|
233
|
+
sourceFields.forEach((field, index) => {
|
|
234
|
+
const value = this.getValueByPath(sourceValues, field.path);
|
|
235
|
+
result = result.replace(
|
|
236
|
+
new RegExp(`\\{${index}\\}`, 'g'),
|
|
237
|
+
String(value ?? '')
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private formatDate(
|
|
244
|
+
value: unknown,
|
|
245
|
+
inputFormat?: string,
|
|
246
|
+
outputFormat?: string
|
|
247
|
+
): string {
|
|
248
|
+
if (!value) return '';
|
|
249
|
+
try {
|
|
250
|
+
const date = new Date(value as string);
|
|
251
|
+
if (isNaN(date.getTime())) return String(value);
|
|
252
|
+
|
|
253
|
+
// Simple format implementation
|
|
254
|
+
const format = outputFormat ?? 'YYYY-MM-DD';
|
|
255
|
+
return format
|
|
256
|
+
.replace('YYYY', date.getFullYear().toString())
|
|
257
|
+
.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'))
|
|
258
|
+
.replace('DD', date.getDate().toString().padStart(2, '0'))
|
|
259
|
+
.replace('HH', date.getHours().toString().padStart(2, '0'))
|
|
260
|
+
.replace('mm', date.getMinutes().toString().padStart(2, '0'))
|
|
261
|
+
.replace('ss', date.getSeconds().toString().padStart(2, '0'));
|
|
262
|
+
} catch {
|
|
263
|
+
return String(value);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private extractDatePart(
|
|
268
|
+
value: unknown,
|
|
269
|
+
part: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'
|
|
270
|
+
): string {
|
|
271
|
+
if (!value) return '';
|
|
272
|
+
try {
|
|
273
|
+
const date = new Date(value as string);
|
|
274
|
+
if (isNaN(date.getTime())) return String(value);
|
|
275
|
+
|
|
276
|
+
switch (part) {
|
|
277
|
+
case 'year':
|
|
278
|
+
return date.getFullYear().toString();
|
|
279
|
+
case 'month':
|
|
280
|
+
return (date.getMonth() + 1).toString().padStart(2, '0');
|
|
281
|
+
case 'day':
|
|
282
|
+
return date.getDate().toString().padStart(2, '0');
|
|
283
|
+
case 'hour':
|
|
284
|
+
return date.getHours().toString().padStart(2, '0');
|
|
285
|
+
case 'minute':
|
|
286
|
+
return date.getMinutes().toString().padStart(2, '0');
|
|
287
|
+
case 'second':
|
|
288
|
+
return date.getSeconds().toString().padStart(2, '0');
|
|
289
|
+
default:
|
|
290
|
+
return String(value);
|
|
291
|
+
}
|
|
292
|
+
} catch {
|
|
293
|
+
return String(value);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private formatNumber(
|
|
298
|
+
value: unknown,
|
|
299
|
+
decimalPlaces?: number,
|
|
300
|
+
prefix?: string,
|
|
301
|
+
suffix?: string
|
|
302
|
+
): string {
|
|
303
|
+
if (value === null || value === undefined) return '';
|
|
304
|
+
const num = Number(value);
|
|
305
|
+
if (isNaN(num)) return String(value);
|
|
306
|
+
|
|
307
|
+
let formatted = decimalPlaces !== undefined
|
|
308
|
+
? num.toFixed(decimalPlaces)
|
|
309
|
+
: num.toString();
|
|
310
|
+
|
|
311
|
+
return `${prefix ?? ''}${formatted}${suffix ?? ''}`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private applyMask(input: string, pattern: string): string {
|
|
315
|
+
if (!pattern) return input;
|
|
316
|
+
|
|
317
|
+
let result = '';
|
|
318
|
+
let inputIndex = 0;
|
|
319
|
+
|
|
320
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
321
|
+
const patternChar = pattern.charAt(i);
|
|
322
|
+
if (patternChar === '#') {
|
|
323
|
+
if (inputIndex < input.length) {
|
|
324
|
+
result += input.charAt(inputIndex);
|
|
325
|
+
inputIndex++;
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
result += patternChar;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
getTransformationLabel(type: TransformationType): string {
|
|
336
|
+
const labels: Record<TransformationType, string> = {
|
|
337
|
+
direct: 'Direct Mapping',
|
|
338
|
+
concat: 'Concatenate',
|
|
339
|
+
substring: 'Substring',
|
|
340
|
+
replace: 'Find & Replace',
|
|
341
|
+
uppercase: 'Uppercase',
|
|
342
|
+
lowercase: 'Lowercase',
|
|
343
|
+
trim: 'Trim',
|
|
344
|
+
mask: 'Mask',
|
|
345
|
+
dateFormat: 'Date Format',
|
|
346
|
+
extractYear: 'Extract Year',
|
|
347
|
+
extractMonth: 'Extract Month',
|
|
348
|
+
extractDay: 'Extract Day',
|
|
349
|
+
extractHour: 'Extract Hour',
|
|
350
|
+
extractMinute: 'Extract Minute',
|
|
351
|
+
extractSecond: 'Extract Second',
|
|
352
|
+
numberFormat: 'Number Format',
|
|
353
|
+
template: 'Template',
|
|
354
|
+
};
|
|
355
|
+
return labels[type];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
getAvailableTransformations(): { type: TransformationType; label: string; category?: string }[] {
|
|
359
|
+
return [
|
|
360
|
+
{ type: 'direct', label: 'Direct Mapping' },
|
|
361
|
+
{ type: 'concat', label: 'Concatenate', category: 'String' },
|
|
362
|
+
{ type: 'substring', label: 'Substring', category: 'String' },
|
|
363
|
+
{ type: 'replace', label: 'Find & Replace', category: 'String' },
|
|
364
|
+
{ type: 'uppercase', label: 'Uppercase', category: 'String' },
|
|
365
|
+
{ type: 'lowercase', label: 'Lowercase', category: 'String' },
|
|
366
|
+
{ type: 'trim', label: 'Trim', category: 'String' },
|
|
367
|
+
{ type: 'mask', label: 'Mask', category: 'String' },
|
|
368
|
+
{ type: 'template', label: 'Template', category: 'String' },
|
|
369
|
+
{ type: 'dateFormat', label: 'Format Date', category: 'Date' },
|
|
370
|
+
{ type: 'extractYear', label: 'Extract Year', category: 'Date' },
|
|
371
|
+
{ type: 'extractMonth', label: 'Extract Month', category: 'Date' },
|
|
372
|
+
{ type: 'extractDay', label: 'Extract Day', category: 'Date' },
|
|
373
|
+
{ type: 'extractHour', label: 'Extract Hour', category: 'Date' },
|
|
374
|
+
{ type: 'extractMinute', label: 'Extract Minute', category: 'Date' },
|
|
375
|
+
{ type: 'extractSecond', label: 'Extract Second', category: 'Date' },
|
|
376
|
+
{ type: 'numberFormat', label: 'Number Format', category: 'Number' },
|
|
377
|
+
];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Condition evaluation methods
|
|
381
|
+
evaluateCondition(value: unknown, condition: FilterGroup): boolean {
|
|
382
|
+
return this.evaluateGroup(value, condition);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private evaluateGroup(value: unknown, group: FilterGroup): boolean {
|
|
386
|
+
if (group.children.length === 0) {
|
|
387
|
+
return true; // Empty group is always true
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (group.logic === 'and') {
|
|
391
|
+
return group.children.every(child => this.evaluateItem(value, child));
|
|
392
|
+
} else {
|
|
393
|
+
return group.children.some(child => this.evaluateItem(value, child));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private evaluateItem(value: unknown, item: FilterItem): boolean {
|
|
398
|
+
if (item.type === 'group') {
|
|
399
|
+
return this.evaluateGroup(value, item);
|
|
400
|
+
} else {
|
|
401
|
+
return this.evaluateConditionItem(value, item);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private evaluateConditionItem(value: unknown, condition: FilterCondition): boolean {
|
|
406
|
+
const strValue = String(value ?? '');
|
|
407
|
+
const numValue = Number(value);
|
|
408
|
+
const condValue = condition.value;
|
|
409
|
+
|
|
410
|
+
switch (condition.operator) {
|
|
411
|
+
case 'equals':
|
|
412
|
+
return strValue === String(condValue);
|
|
413
|
+
case 'notEquals':
|
|
414
|
+
return strValue !== String(condValue);
|
|
415
|
+
case 'contains':
|
|
416
|
+
return strValue.includes(String(condValue));
|
|
417
|
+
case 'notContains':
|
|
418
|
+
return !strValue.includes(String(condValue));
|
|
419
|
+
case 'startsWith':
|
|
420
|
+
return strValue.startsWith(String(condValue));
|
|
421
|
+
case 'endsWith':
|
|
422
|
+
return strValue.endsWith(String(condValue));
|
|
423
|
+
case 'isEmpty':
|
|
424
|
+
return strValue === '' || value === null || value === undefined;
|
|
425
|
+
case 'isNotEmpty':
|
|
426
|
+
return strValue !== '' && value !== null && value !== undefined;
|
|
427
|
+
case 'greaterThan':
|
|
428
|
+
return !isNaN(numValue) && numValue > Number(condValue);
|
|
429
|
+
case 'lessThan':
|
|
430
|
+
return !isNaN(numValue) && numValue < Number(condValue);
|
|
431
|
+
case 'greaterThanOrEqual':
|
|
432
|
+
return !isNaN(numValue) && numValue >= Number(condValue);
|
|
433
|
+
case 'lessThanOrEqual':
|
|
434
|
+
return !isNaN(numValue) && numValue <= Number(condValue);
|
|
435
|
+
case 'isTrue':
|
|
436
|
+
return value === true || strValue.toLowerCase() === 'true';
|
|
437
|
+
case 'isFalse':
|
|
438
|
+
return value === false || strValue.toLowerCase() === 'false';
|
|
439
|
+
default:
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Check if a transformation's condition is met
|
|
446
|
+
*/
|
|
447
|
+
isConditionMet(value: unknown, config: TransformationConfig): boolean {
|
|
448
|
+
if (!config.condition?.enabled || !config.condition.root) {
|
|
449
|
+
return true; // No condition means always apply
|
|
450
|
+
}
|
|
451
|
+
return this.evaluateCondition(value, config.condition.root);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Public API Surface of ngx-data-mapper
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Models
|
|
6
|
+
export * from './lib/models/schema.model';
|
|
7
|
+
export * from './lib/models/json-schema.model';
|
|
8
|
+
|
|
9
|
+
// Services
|
|
10
|
+
export * from './lib/services/mapping.service';
|
|
11
|
+
export * from './lib/services/transformation.service';
|
|
12
|
+
export * from './lib/services/svg-connector.service';
|
|
13
|
+
export * from './lib/services/schema-parser.service';
|
|
14
|
+
|
|
15
|
+
// Components
|
|
16
|
+
export * from './lib/components/data-mapper/data-mapper.component';
|
|
17
|
+
export * from './lib/components/schema-editor/schema-editor.component';
|
|
18
|
+
export * from './lib/components/schema-tree/schema-tree.component';
|
|
19
|
+
export * from './lib/components/transformation-popover/transformation-popover.component';
|
|
20
|
+
export * from './lib/components/array-filter-modal/array-filter-modal.component';
|
|
21
|
+
export * from './lib/components/array-selector-modal/array-selector-modal.component';
|
|
22
|
+
export * from './lib/components/default-value-popover/default-value-popover.component';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compileOnSave": false,
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "./",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"noImplicitOverride": true,
|
|
7
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
8
|
+
"noImplicitReturns": true,
|
|
9
|
+
"noFallthroughCasesInSwitch": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"experimentalDecorators": true,
|
|
13
|
+
"importHelpers": true,
|
|
14
|
+
"target": "ES2022",
|
|
15
|
+
"module": "preserve",
|
|
16
|
+
"paths": {
|
|
17
|
+
"@expeed/ngx-data-mapper": [
|
|
18
|
+
"projects/ngx-data-mapper/src/public-api.ts"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"angularCompilerOptions": {
|
|
23
|
+
"enableI18nLegacyMessageIdFormat": false,
|
|
24
|
+
"strictInjectionParameters": true,
|
|
25
|
+
"strictInputAccessModifiers": true,
|
|
26
|
+
"strictTemplates": true
|
|
27
|
+
}
|
|
28
|
+
}
|