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,270 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { SchemaField, SchemaDefinition } from '../models/schema.model';
|
|
3
|
+
import { JsonSchema } from '../models/json-schema.model';
|
|
4
|
+
|
|
5
|
+
// Extended schema document with filtering options
|
|
6
|
+
export interface SchemaDocument extends JsonSchema {
|
|
7
|
+
$ref?: string;
|
|
8
|
+
$defs?: Record<string, JsonSchema>; // Alternative to definitions
|
|
9
|
+
exclude?: string[];
|
|
10
|
+
include?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ModelRegistry {
|
|
14
|
+
[modelName: string]: JsonSchema;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Injectable({
|
|
18
|
+
providedIn: 'root',
|
|
19
|
+
})
|
|
20
|
+
export class SchemaParserService {
|
|
21
|
+
private modelRegistry: ModelRegistry = {};
|
|
22
|
+
private idCounter = 0;
|
|
23
|
+
|
|
24
|
+
registerModels(models: ModelRegistry): void {
|
|
25
|
+
this.modelRegistry = { ...this.modelRegistry, ...models };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clearRegistry(): void {
|
|
29
|
+
this.modelRegistry = {};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
parseSchema(
|
|
33
|
+
schemaJson: string | SchemaDocument,
|
|
34
|
+
schemaName: string = 'Schema'
|
|
35
|
+
): SchemaDefinition {
|
|
36
|
+
const schema: SchemaDocument =
|
|
37
|
+
typeof schemaJson === 'string' ? JSON.parse(schemaJson) : schemaJson;
|
|
38
|
+
|
|
39
|
+
this.idCounter = 0;
|
|
40
|
+
|
|
41
|
+
// Extract definitions from the schema document itself
|
|
42
|
+
const localDefs = schema.$defs || schema.definitions || {};
|
|
43
|
+
const combinedRegistry = { ...this.modelRegistry, ...localDefs };
|
|
44
|
+
|
|
45
|
+
let resolvedSchema: JsonSchema;
|
|
46
|
+
|
|
47
|
+
if (schema.$ref) {
|
|
48
|
+
// Resolve the reference
|
|
49
|
+
resolvedSchema = this.resolveRef(schema.$ref, combinedRegistry);
|
|
50
|
+
} else if (schema.properties) {
|
|
51
|
+
// Direct schema with properties
|
|
52
|
+
resolvedSchema = schema as JsonSchema;
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error('Schema must have either $ref or properties');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Build fields from the resolved schema
|
|
58
|
+
let fields = this.buildFields(resolvedSchema, combinedRegistry, '');
|
|
59
|
+
|
|
60
|
+
// Apply exclude filter
|
|
61
|
+
if (schema.exclude && schema.exclude.length > 0) {
|
|
62
|
+
fields = this.applyExclude(fields, schema.exclude);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Apply include filter (only if specified)
|
|
66
|
+
if (schema.include && schema.include.length > 0) {
|
|
67
|
+
fields = this.applyInclude(fields, schema.include);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
name: schema.title || schemaName,
|
|
72
|
+
fields,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private resolveRef(
|
|
77
|
+
ref: string,
|
|
78
|
+
registry: Record<string, JsonSchema>
|
|
79
|
+
): JsonSchema {
|
|
80
|
+
// Handle different ref formats:
|
|
81
|
+
// #model, #/definitions/model, #/$defs/model, model
|
|
82
|
+
let modelName: string;
|
|
83
|
+
|
|
84
|
+
if (ref.startsWith('#/$defs/')) {
|
|
85
|
+
modelName = ref.substring(8);
|
|
86
|
+
} else if (ref.startsWith('#/definitions/')) {
|
|
87
|
+
modelName = ref.substring(14);
|
|
88
|
+
} else if (ref.startsWith('#')) {
|
|
89
|
+
modelName = ref.substring(1);
|
|
90
|
+
} else {
|
|
91
|
+
modelName = ref;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const resolved = registry[modelName];
|
|
95
|
+
if (!resolved) {
|
|
96
|
+
throw new Error(`Cannot resolve reference: ${ref}. Model "${modelName}" not found in registry.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If the resolved schema also has a $ref, resolve it recursively
|
|
100
|
+
if (resolved.$ref) {
|
|
101
|
+
return this.resolveRef(resolved.$ref, registry);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private buildFields(
|
|
108
|
+
schema: JsonSchema,
|
|
109
|
+
registry: Record<string, JsonSchema>,
|
|
110
|
+
parentPath: string,
|
|
111
|
+
arrayContext?: { isArrayItem: boolean; parentArrayPath: string }
|
|
112
|
+
): SchemaField[] {
|
|
113
|
+
const fields: SchemaField[] = [];
|
|
114
|
+
|
|
115
|
+
if (!schema.properties) {
|
|
116
|
+
return fields;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const [name, propSchema] of Object.entries(schema.properties)) {
|
|
120
|
+
const path = parentPath ? `${parentPath}.${name}` : name;
|
|
121
|
+
const field = this.buildField(name, propSchema, registry, path, arrayContext);
|
|
122
|
+
fields.push(field);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return fields;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private buildField(
|
|
129
|
+
name: string,
|
|
130
|
+
schema: JsonSchema,
|
|
131
|
+
registry: Record<string, JsonSchema>,
|
|
132
|
+
path: string,
|
|
133
|
+
arrayContext?: { isArrayItem: boolean; parentArrayPath: string }
|
|
134
|
+
): SchemaField {
|
|
135
|
+
// Resolve $ref if present
|
|
136
|
+
let resolvedSchema = schema;
|
|
137
|
+
if (schema.$ref) {
|
|
138
|
+
resolvedSchema = { ...this.resolveRef(schema.$ref, registry), ...schema };
|
|
139
|
+
delete resolvedSchema.$ref;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const fieldType = this.mapType(resolvedSchema);
|
|
143
|
+
const field: SchemaField = {
|
|
144
|
+
id: `field-${++this.idCounter}-${name}`,
|
|
145
|
+
name,
|
|
146
|
+
type: fieldType,
|
|
147
|
+
path,
|
|
148
|
+
description: resolvedSchema.description,
|
|
149
|
+
isArrayItem: arrayContext?.isArrayItem,
|
|
150
|
+
parentArrayPath: arrayContext?.parentArrayPath,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Handle nested objects
|
|
154
|
+
if (fieldType === 'object' && resolvedSchema.properties) {
|
|
155
|
+
field.children = this.buildFields(resolvedSchema, registry, path, arrayContext);
|
|
156
|
+
field.expanded = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Handle arrays with object items
|
|
160
|
+
if (fieldType === 'array' && resolvedSchema.items) {
|
|
161
|
+
let itemSchema = resolvedSchema.items;
|
|
162
|
+
if (itemSchema.$ref) {
|
|
163
|
+
itemSchema = this.resolveRef(itemSchema.$ref, registry);
|
|
164
|
+
}
|
|
165
|
+
if (itemSchema.properties) {
|
|
166
|
+
// Mark children as array items with reference to parent array
|
|
167
|
+
field.children = this.buildFields(itemSchema, registry, `${path}[]`, {
|
|
168
|
+
isArrayItem: true,
|
|
169
|
+
parentArrayPath: path,
|
|
170
|
+
});
|
|
171
|
+
field.expanded = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return field;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private mapType(
|
|
179
|
+
schema: JsonSchema
|
|
180
|
+
): 'string' | 'number' | 'boolean' | 'object' | 'array' | 'date' {
|
|
181
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
182
|
+
const format = schema.format;
|
|
183
|
+
|
|
184
|
+
// Check format first for date types
|
|
185
|
+
if (format === 'date' || format === 'date-time' || format === 'time') {
|
|
186
|
+
return 'date';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
switch (type) {
|
|
190
|
+
case 'string':
|
|
191
|
+
return 'string';
|
|
192
|
+
case 'number':
|
|
193
|
+
case 'integer':
|
|
194
|
+
return 'number';
|
|
195
|
+
case 'boolean':
|
|
196
|
+
return 'boolean';
|
|
197
|
+
case 'object':
|
|
198
|
+
return 'object';
|
|
199
|
+
case 'array':
|
|
200
|
+
return 'array';
|
|
201
|
+
default:
|
|
202
|
+
// If type is not specified but has properties, it's an object
|
|
203
|
+
if (schema.properties) {
|
|
204
|
+
return 'object';
|
|
205
|
+
}
|
|
206
|
+
return 'string';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private applyExclude(fields: SchemaField[], exclude: string[]): SchemaField[] {
|
|
211
|
+
return fields
|
|
212
|
+
.filter((field) => !exclude.includes(field.name) && !exclude.includes(field.path))
|
|
213
|
+
.map((field) => {
|
|
214
|
+
if (field.children) {
|
|
215
|
+
// Filter nested fields by checking both full path and relative name
|
|
216
|
+
const filteredChildren = this.applyExclude(field.children, exclude);
|
|
217
|
+
return { ...field, children: filteredChildren };
|
|
218
|
+
}
|
|
219
|
+
return field;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private applyInclude(fields: SchemaField[], include: string[]): SchemaField[] {
|
|
224
|
+
return fields
|
|
225
|
+
.filter((field) => {
|
|
226
|
+
// Include if field name or path matches
|
|
227
|
+
if (include.includes(field.name) || include.includes(field.path)) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
// Include if any child path matches
|
|
231
|
+
if (field.children) {
|
|
232
|
+
return this.hasIncludedChild(field.children, include);
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
})
|
|
236
|
+
.map((field) => {
|
|
237
|
+
if (field.children) {
|
|
238
|
+
// Keep parent but filter children
|
|
239
|
+
const filteredChildren = this.applyInclude(field.children, include);
|
|
240
|
+
return { ...field, children: filteredChildren.length > 0 ? filteredChildren : field.children };
|
|
241
|
+
}
|
|
242
|
+
return field;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private hasIncludedChild(fields: SchemaField[], include: string[]): boolean {
|
|
247
|
+
return fields.some((field) => {
|
|
248
|
+
if (include.includes(field.name) || include.includes(field.path)) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
if (field.children) {
|
|
252
|
+
return this.hasIncludedChild(field.children, include);
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Utility method to create a schema document from model name
|
|
259
|
+
createSchemaFromRef(
|
|
260
|
+
modelRef: string,
|
|
261
|
+
options?: { exclude?: string[]; include?: string[]; title?: string }
|
|
262
|
+
): SchemaDocument {
|
|
263
|
+
return {
|
|
264
|
+
$ref: modelRef,
|
|
265
|
+
title: options?.title,
|
|
266
|
+
exclude: options?.exclude,
|
|
267
|
+
include: options?.include,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
export interface Point {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ConnectionPath {
|
|
9
|
+
id: string;
|
|
10
|
+
mappingId: string;
|
|
11
|
+
path: string;
|
|
12
|
+
sourcePoints: Point[];
|
|
13
|
+
targetPoint: Point;
|
|
14
|
+
midPoint: Point;
|
|
15
|
+
isSelected: boolean;
|
|
16
|
+
hasTransformation: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Injectable({
|
|
20
|
+
providedIn: 'root',
|
|
21
|
+
})
|
|
22
|
+
export class SvgConnectorService {
|
|
23
|
+
createBezierPath(start: Point, end: Point): string {
|
|
24
|
+
const dx = end.x - start.x;
|
|
25
|
+
const controlPointOffset = Math.min(Math.abs(dx) * 0.5, 150);
|
|
26
|
+
|
|
27
|
+
const cp1x = start.x + controlPointOffset;
|
|
28
|
+
const cp1y = start.y;
|
|
29
|
+
const cp2x = end.x - controlPointOffset;
|
|
30
|
+
const cp2y = end.y;
|
|
31
|
+
|
|
32
|
+
return `M ${start.x} ${start.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${end.x} ${end.y}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
createMultiSourcePath(sources: Point[], target: Point): { paths: string[]; mergePoint: Point } {
|
|
36
|
+
const mergeX = target.x - 80;
|
|
37
|
+
const mergeY = target.y;
|
|
38
|
+
const mergePoint = { x: mergeX, y: mergeY };
|
|
39
|
+
|
|
40
|
+
const paths = sources.map((source) => {
|
|
41
|
+
// Path from source to merge point
|
|
42
|
+
const dx1 = mergeX - source.x;
|
|
43
|
+
const cp1Offset = Math.min(Math.abs(dx1) * 0.4, 100);
|
|
44
|
+
|
|
45
|
+
return `M ${source.x} ${source.y} C ${source.x + cp1Offset} ${source.y}, ${mergeX - cp1Offset} ${mergeY}, ${mergeX} ${mergeY}`;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Add path from merge point to target
|
|
49
|
+
const finalPath = `M ${mergeX} ${mergeY} L ${target.x} ${target.y}`;
|
|
50
|
+
paths.push(finalPath);
|
|
51
|
+
|
|
52
|
+
return { paths, mergePoint };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getMidPoint(start: Point, end: Point): Point {
|
|
56
|
+
return {
|
|
57
|
+
x: (start.x + end.x) / 2,
|
|
58
|
+
y: (start.y + end.y) / 2,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getMultiSourceMidPoint(sources: Point[], target: Point): Point {
|
|
63
|
+
const mergeX = target.x - 80;
|
|
64
|
+
return {
|
|
65
|
+
x: mergeX,
|
|
66
|
+
y: target.y,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
calculateConnectionPoint(
|
|
71
|
+
rect: DOMRect,
|
|
72
|
+
side: 'source' | 'target',
|
|
73
|
+
containerRect: DOMRect
|
|
74
|
+
): Point {
|
|
75
|
+
const relativeY = rect.top - containerRect.top + rect.height / 2;
|
|
76
|
+
// Offset to position endpoint circles outside the schema panels
|
|
77
|
+
const endpointOffset = 8;
|
|
78
|
+
|
|
79
|
+
if (side === 'source') {
|
|
80
|
+
return {
|
|
81
|
+
x: rect.right - containerRect.left + endpointOffset,
|
|
82
|
+
y: relativeY,
|
|
83
|
+
};
|
|
84
|
+
} else {
|
|
85
|
+
return {
|
|
86
|
+
x: rect.left - containerRect.left - endpointOffset,
|
|
87
|
+
y: relativeY,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
isPointNearPath(
|
|
93
|
+
point: Point,
|
|
94
|
+
pathStart: Point,
|
|
95
|
+
pathEnd: Point,
|
|
96
|
+
threshold: number = 10
|
|
97
|
+
): boolean {
|
|
98
|
+
// Simplified hit detection using distance to line segment
|
|
99
|
+
const A = point.x - pathStart.x;
|
|
100
|
+
const B = point.y - pathStart.y;
|
|
101
|
+
const C = pathEnd.x - pathStart.x;
|
|
102
|
+
const D = pathEnd.y - pathStart.y;
|
|
103
|
+
|
|
104
|
+
const dot = A * C + B * D;
|
|
105
|
+
const lenSq = C * C + D * D;
|
|
106
|
+
let param = -1;
|
|
107
|
+
|
|
108
|
+
if (lenSq !== 0) {
|
|
109
|
+
param = dot / lenSq;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let xx: number, yy: number;
|
|
113
|
+
|
|
114
|
+
if (param < 0) {
|
|
115
|
+
xx = pathStart.x;
|
|
116
|
+
yy = pathStart.y;
|
|
117
|
+
} else if (param > 1) {
|
|
118
|
+
xx = pathEnd.x;
|
|
119
|
+
yy = pathEnd.y;
|
|
120
|
+
} else {
|
|
121
|
+
xx = pathStart.x + param * C;
|
|
122
|
+
yy = pathStart.y + param * D;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const dx = point.x - xx;
|
|
126
|
+
const dy = point.y - yy;
|
|
127
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
128
|
+
|
|
129
|
+
return distance <= threshold;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
createDragPath(start: Point, end: Point): string {
|
|
133
|
+
return this.createBezierPath(start, end);
|
|
134
|
+
}
|
|
135
|
+
}
|