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,346 @@
|
|
|
1
|
+
<div class="transformation-popover" [ngStyle]="getPopoverStyle()">
|
|
2
|
+
<div class="popover-arrow"></div>
|
|
3
|
+
|
|
4
|
+
<div class="popover-header">
|
|
5
|
+
<span class="popover-title">Transformation</span>
|
|
6
|
+
<button mat-icon-button class="close-btn" (click)="onClose()">
|
|
7
|
+
<mat-icon>close</mat-icon>
|
|
8
|
+
</button>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="popover-content">
|
|
12
|
+
<!-- Source/Target Info -->
|
|
13
|
+
<div class="mapping-info">
|
|
14
|
+
<div class="info-row">
|
|
15
|
+
<span class="info-label">Source:</span>
|
|
16
|
+
<span class="info-value">{{ getSourceFieldNames() }}</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="info-row">
|
|
19
|
+
<span class="info-label">Target:</span>
|
|
20
|
+
<span class="info-value">{{ mapping.targetField.name }}</span>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Single Step Mode (default, clean UI) -->
|
|
25
|
+
@if (!isMultiStep) {
|
|
26
|
+
<ng-container *ngTemplateOutlet="stepConfig; context: { step: steps[0], index: 0, showHeader: false }"></ng-container>
|
|
27
|
+
|
|
28
|
+
<!-- Preview Section -->
|
|
29
|
+
<div class="preview-section">
|
|
30
|
+
<span class="preview-label">Preview:</span>
|
|
31
|
+
<div class="preview-value">{{ stepPreviews[0] || '(empty)' }}</div>
|
|
32
|
+
</div>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
<!-- Multi-Step Mode -->
|
|
36
|
+
@if (isMultiStep) {
|
|
37
|
+
<div class="steps-container" cdkDropList (cdkDropListDropped)="onStepDrop($event)">
|
|
38
|
+
@for (step of steps; track $index; let i = $index) {
|
|
39
|
+
<div class="step-card" [class.expanded]="isStepExpanded(i)" cdkDrag>
|
|
40
|
+
<!-- Collapsed View -->
|
|
41
|
+
@if (!isStepExpanded(i)) {
|
|
42
|
+
<div class="step-collapsed" (click)="toggleStep(i)">
|
|
43
|
+
<mat-icon class="drag-handle" cdkDragHandle (click)="$event.stopPropagation()">drag_indicator</mat-icon>
|
|
44
|
+
<div class="step-collapsed-header">
|
|
45
|
+
<div class="step-title-row">
|
|
46
|
+
<span class="step-number">Step {{ i + 1 }}: {{ getStepTypeLabel(step.type) }}</span>
|
|
47
|
+
@if (hasCondition(step)) {
|
|
48
|
+
<span class="condition-badge" matTooltip="{{ getConditionSummary(step) }}">
|
|
49
|
+
<mat-icon>filter_alt</mat-icon>
|
|
50
|
+
if
|
|
51
|
+
</span>
|
|
52
|
+
}
|
|
53
|
+
</div>
|
|
54
|
+
<div class="step-collapsed-actions">
|
|
55
|
+
<button
|
|
56
|
+
mat-icon-button
|
|
57
|
+
class="expand-btn"
|
|
58
|
+
(click)="toggleStep(i); $event.stopPropagation()"
|
|
59
|
+
matTooltip="Edit step"
|
|
60
|
+
>
|
|
61
|
+
<mat-icon>edit</mat-icon>
|
|
62
|
+
</button>
|
|
63
|
+
<button
|
|
64
|
+
mat-icon-button
|
|
65
|
+
class="remove-step-btn"
|
|
66
|
+
(click)="removeStep(i); $event.stopPropagation()"
|
|
67
|
+
matTooltip="Remove step"
|
|
68
|
+
>
|
|
69
|
+
<mat-icon>close</mat-icon>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="step-collapsed-preview">
|
|
74
|
+
<span class="step-input">"{{ (stepInputs[i] || '') | slice:0:20 }}{{ (stepInputs[i] || '').length > 20 ? '...' : '' }}"</span>
|
|
75
|
+
<mat-icon class="arrow-icon">arrow_forward</mat-icon>
|
|
76
|
+
<span class="step-output">"{{ (stepPreviews[i] || '') | slice:0:20 }}{{ (stepPreviews[i] || '').length > 20 ? '...' : '' }}"</span>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
<!-- Expanded View -->
|
|
82
|
+
@if (isStepExpanded(i)) {
|
|
83
|
+
<div class="step-expanded">
|
|
84
|
+
<mat-icon class="drag-handle" cdkDragHandle>drag_indicator</mat-icon>
|
|
85
|
+
<div class="step-header">
|
|
86
|
+
<span class="step-number">Step {{ i + 1 }}</span>
|
|
87
|
+
<div class="step-header-actions">
|
|
88
|
+
<button
|
|
89
|
+
mat-icon-button
|
|
90
|
+
class="collapse-btn"
|
|
91
|
+
(click)="toggleStep(i)"
|
|
92
|
+
matTooltip="Collapse"
|
|
93
|
+
>
|
|
94
|
+
<mat-icon>expand_less</mat-icon>
|
|
95
|
+
</button>
|
|
96
|
+
<button
|
|
97
|
+
mat-icon-button
|
|
98
|
+
class="remove-step-btn"
|
|
99
|
+
(click)="removeStep(i)"
|
|
100
|
+
matTooltip="Remove step"
|
|
101
|
+
>
|
|
102
|
+
<mat-icon>close</mat-icon>
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="step-content">
|
|
108
|
+
<ng-container *ngTemplateOutlet="stepConfig; context: { step: step, index: i, showHeader: false }"></ng-container>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- Step Preview -->
|
|
112
|
+
<div class="step-preview">
|
|
113
|
+
<mat-icon class="preview-arrow">arrow_downward</mat-icon>
|
|
114
|
+
<span class="step-preview-value">{{ stepPreviews[i] || '(empty)' }}</span>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
}
|
|
118
|
+
</div>
|
|
119
|
+
}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<!-- Final Preview -->
|
|
123
|
+
<div class="preview-section final-preview">
|
|
124
|
+
<span class="preview-label">Final Result:</span>
|
|
125
|
+
<div class="preview-value">{{ finalPreview || '(empty)' }}</div>
|
|
126
|
+
</div>
|
|
127
|
+
}
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<!-- Add Step Button - Always visible -->
|
|
131
|
+
<div class="add-step-section">
|
|
132
|
+
<button mat-stroked-button class="add-step-btn" (click)="addStep()">
|
|
133
|
+
<mat-icon>add</mat-icon>
|
|
134
|
+
@if (isMultiStep) {
|
|
135
|
+
Add Step
|
|
136
|
+
} @else {
|
|
137
|
+
Add Transformation Step
|
|
138
|
+
}
|
|
139
|
+
</button>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div class="popover-actions">
|
|
143
|
+
<button mat-button color="warn" (click)="onDelete()" matTooltip="Remove this mapping">
|
|
144
|
+
<mat-icon>delete</mat-icon>
|
|
145
|
+
Delete
|
|
146
|
+
</button>
|
|
147
|
+
<div class="action-spacer"></div>
|
|
148
|
+
<button mat-button (click)="onClose()">Cancel</button>
|
|
149
|
+
<button mat-flat-button color="primary" (click)="onSave()">Apply</button>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- Backdrop -->
|
|
154
|
+
<div class="popover-backdrop" (click)="onClose()"></div>
|
|
155
|
+
|
|
156
|
+
<!-- Step Configuration Template -->
|
|
157
|
+
<ng-template #stepConfig let-step="step" let-index="index" let-showHeader="showHeader">
|
|
158
|
+
<!-- Transformation Type -->
|
|
159
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
160
|
+
<mat-label>Transformation Type</mat-label>
|
|
161
|
+
<mat-select [(ngModel)]="step.type" (selectionChange)="onStepTypeChange(index)">
|
|
162
|
+
@for (t of availableTransformations; track t.type) {
|
|
163
|
+
<mat-option [value]="t.type">{{ t.label }}</mat-option>
|
|
164
|
+
}
|
|
165
|
+
</mat-select>
|
|
166
|
+
</mat-form-field>
|
|
167
|
+
|
|
168
|
+
<!-- Type-specific options -->
|
|
169
|
+
@switch (step.type) {
|
|
170
|
+
@case ('concat') {
|
|
171
|
+
<div class="config-section">
|
|
172
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
173
|
+
<mat-label>Separator</mat-label>
|
|
174
|
+
<input
|
|
175
|
+
matInput
|
|
176
|
+
[(ngModel)]="step.separator"
|
|
177
|
+
(ngModelChange)="onConfigChange()"
|
|
178
|
+
placeholder=" "
|
|
179
|
+
/>
|
|
180
|
+
<mat-hint>Join values with this (default: space)</mat-hint>
|
|
181
|
+
</mat-form-field>
|
|
182
|
+
<div class="or-divider">or use template for custom format</div>
|
|
183
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
184
|
+
<mat-label>Template</mat-label>
|
|
185
|
+
<input
|
|
186
|
+
matInput
|
|
187
|
+
[(ngModel)]="step.template"
|
|
188
|
+
(ngModelChange)="onConfigChange()"
|
|
189
|
+
[placeholder]="'{0} - {1}'"
|
|
190
|
+
/>
|
|
191
|
+
<mat-hint>Overrides separator if set</mat-hint>
|
|
192
|
+
</mat-form-field>
|
|
193
|
+
</div>
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@case ('substring') {
|
|
197
|
+
<div class="config-section config-row">
|
|
198
|
+
<mat-form-field appearance="outline">
|
|
199
|
+
<mat-label>Start Index</mat-label>
|
|
200
|
+
<input
|
|
201
|
+
matInput
|
|
202
|
+
type="number"
|
|
203
|
+
[(ngModel)]="step.startIndex"
|
|
204
|
+
(ngModelChange)="onConfigChange()"
|
|
205
|
+
min="0"
|
|
206
|
+
/>
|
|
207
|
+
</mat-form-field>
|
|
208
|
+
<mat-form-field appearance="outline">
|
|
209
|
+
<mat-label>End Index</mat-label>
|
|
210
|
+
<input
|
|
211
|
+
matInput
|
|
212
|
+
type="number"
|
|
213
|
+
[(ngModel)]="step.endIndex"
|
|
214
|
+
(ngModelChange)="onConfigChange()"
|
|
215
|
+
/>
|
|
216
|
+
</mat-form-field>
|
|
217
|
+
</div>
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@case ('replace') {
|
|
221
|
+
<div class="config-section">
|
|
222
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
223
|
+
<mat-label>Search For</mat-label>
|
|
224
|
+
<input
|
|
225
|
+
matInput
|
|
226
|
+
[(ngModel)]="step.searchValue"
|
|
227
|
+
(ngModelChange)="onConfigChange()"
|
|
228
|
+
/>
|
|
229
|
+
</mat-form-field>
|
|
230
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
231
|
+
<mat-label>Replace With</mat-label>
|
|
232
|
+
<input
|
|
233
|
+
matInput
|
|
234
|
+
[(ngModel)]="step.replaceValue"
|
|
235
|
+
(ngModelChange)="onConfigChange()"
|
|
236
|
+
/>
|
|
237
|
+
</mat-form-field>
|
|
238
|
+
</div>
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@case ('dateFormat') {
|
|
242
|
+
<div class="config-section">
|
|
243
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
244
|
+
<mat-label>Output Format</mat-label>
|
|
245
|
+
<input
|
|
246
|
+
matInput
|
|
247
|
+
[(ngModel)]="step.outputFormat"
|
|
248
|
+
(ngModelChange)="onConfigChange()"
|
|
249
|
+
placeholder="YYYY-MM-DD"
|
|
250
|
+
/>
|
|
251
|
+
<mat-hint>YYYY, MM, DD, HH, mm, ss</mat-hint>
|
|
252
|
+
</mat-form-field>
|
|
253
|
+
</div>
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@case ('numberFormat') {
|
|
257
|
+
<div class="config-section">
|
|
258
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
259
|
+
<mat-label>Decimal Places</mat-label>
|
|
260
|
+
<input
|
|
261
|
+
matInput
|
|
262
|
+
type="number"
|
|
263
|
+
[(ngModel)]="step.decimalPlaces"
|
|
264
|
+
(ngModelChange)="onConfigChange()"
|
|
265
|
+
min="0"
|
|
266
|
+
/>
|
|
267
|
+
</mat-form-field>
|
|
268
|
+
<div class="config-row">
|
|
269
|
+
<mat-form-field appearance="outline">
|
|
270
|
+
<mat-label>Prefix</mat-label>
|
|
271
|
+
<input
|
|
272
|
+
matInput
|
|
273
|
+
[(ngModel)]="step.prefix"
|
|
274
|
+
(ngModelChange)="onConfigChange()"
|
|
275
|
+
placeholder="$"
|
|
276
|
+
/>
|
|
277
|
+
</mat-form-field>
|
|
278
|
+
<mat-form-field appearance="outline">
|
|
279
|
+
<mat-label>Suffix</mat-label>
|
|
280
|
+
<input
|
|
281
|
+
matInput
|
|
282
|
+
[(ngModel)]="step.suffix"
|
|
283
|
+
(ngModelChange)="onConfigChange()"
|
|
284
|
+
/>
|
|
285
|
+
</mat-form-field>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@case ('mask') {
|
|
291
|
+
<div class="config-section">
|
|
292
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
293
|
+
<mat-label>Pattern</mat-label>
|
|
294
|
+
<input
|
|
295
|
+
matInput
|
|
296
|
+
[(ngModel)]="step.pattern"
|
|
297
|
+
(ngModelChange)="onConfigChange()"
|
|
298
|
+
placeholder="(###) ###-####"
|
|
299
|
+
/>
|
|
300
|
+
<mat-hint># = character from input</mat-hint>
|
|
301
|
+
</mat-form-field>
|
|
302
|
+
</div>
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@case ('template') {
|
|
306
|
+
<div class="config-section">
|
|
307
|
+
<mat-form-field appearance="outline" class="full-width">
|
|
308
|
+
<mat-label>Template Expression</mat-label>
|
|
309
|
+
<textarea
|
|
310
|
+
matInput
|
|
311
|
+
[(ngModel)]="step.template"
|
|
312
|
+
(ngModelChange)="onConfigChange()"
|
|
313
|
+
rows="3"
|
|
314
|
+
[placeholder]="'Hello {0}, your ID is {1}'"
|
|
315
|
+
></textarea>
|
|
316
|
+
@if (index === 0) {
|
|
317
|
+
<mat-hint>Use {{ '{' }}0{{ '}' }}, {{ '{' }}1{{ '}' }}, etc. for source fields</mat-hint>
|
|
318
|
+
} @else {
|
|
319
|
+
<mat-hint>Use {{ '{' }}0{{ '}' }} for the value from previous step</mat-hint>
|
|
320
|
+
}
|
|
321
|
+
</mat-form-field>
|
|
322
|
+
</div>
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
<!-- Condition Section -->
|
|
327
|
+
<div class="condition-section">
|
|
328
|
+
<mat-checkbox
|
|
329
|
+
[checked]="hasCondition(step)"
|
|
330
|
+
(change)="toggleCondition(step, $event.checked)"
|
|
331
|
+
class="condition-checkbox"
|
|
332
|
+
>
|
|
333
|
+
Apply conditionally
|
|
334
|
+
</mat-checkbox>
|
|
335
|
+
|
|
336
|
+
@if (hasCondition(step)) {
|
|
337
|
+
<div class="condition-content">
|
|
338
|
+
<condition-builder
|
|
339
|
+
[condition]="step.condition?.root || null"
|
|
340
|
+
[compact]="true"
|
|
341
|
+
(conditionChange)="onConditionChange(step, $event)"
|
|
342
|
+
></condition-builder>
|
|
343
|
+
</div>
|
|
344
|
+
}
|
|
345
|
+
</div>
|
|
346
|
+
</ng-template>
|