ngx-vflow 1.1.2 → 1.2.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/esm2022/lib/vflow/components/edge-label/edge-label.component.mjs +25 -6
- package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +10 -2
- package/esm2022/lib/vflow/interfaces/edge-label.interface.mjs +1 -1
- package/esm2022/lib/vflow/services/draggable.service.mjs +34 -12
- package/esm2022/lib/vflow/services/flow-settings.service.mjs +2 -1
- package/fesm2022/ngx-vflow.mjs +65 -17
- package/fesm2022/ngx-vflow.mjs.map +1 -1
- package/lib/vflow/components/edge-label/edge-label.component.d.ts +4 -2
- package/lib/vflow/components/vflow/vflow.component.d.ts +8 -1
- package/lib/vflow/interfaces/edge-label.interface.d.ts +12 -3
- package/lib/vflow/services/draggable.service.d.ts +9 -0
- package/lib/vflow/services/flow-settings.service.d.ts +1 -0
- package/package.json +1 -1
package/fesm2022/ngx-vflow.mjs
CHANGED
|
@@ -165,6 +165,7 @@ class FlowSettingsService {
|
|
|
165
165
|
this.minZoom = signal(0.5);
|
|
166
166
|
this.maxZoom = signal(3);
|
|
167
167
|
this.background = signal({ type: 'solid', color: '#fff' });
|
|
168
|
+
this.snapGrid = signal([1, 1]);
|
|
168
169
|
}
|
|
169
170
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FlowSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
170
171
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FlowSettingsService }); }
|
|
@@ -459,6 +460,7 @@ const round = (num) => Math.round(num * 100) / 100;
|
|
|
459
460
|
class DraggableService {
|
|
460
461
|
constructor() {
|
|
461
462
|
this.entitiesService = inject(FlowEntitiesService);
|
|
463
|
+
this.settingsService = inject(FlowSettingsService);
|
|
462
464
|
}
|
|
463
465
|
/**
|
|
464
466
|
* Enable draggable behavior for element.
|
|
@@ -517,7 +519,7 @@ class DraggableService {
|
|
|
517
519
|
x: round(event.x + initialPositions[index].x),
|
|
518
520
|
y: round(event.y + initialPositions[index].y),
|
|
519
521
|
};
|
|
520
|
-
moveNode(model, point);
|
|
522
|
+
this.moveNode(model, point);
|
|
521
523
|
});
|
|
522
524
|
});
|
|
523
525
|
}
|
|
@@ -530,22 +532,42 @@ class DraggableService {
|
|
|
530
532
|
: // we only can move current node if it's not selected
|
|
531
533
|
[model];
|
|
532
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* @todo make it unit testable
|
|
537
|
+
*/
|
|
538
|
+
moveNode(model, point) {
|
|
539
|
+
point = this.alignToGrid(point);
|
|
540
|
+
const parent = model.parent();
|
|
541
|
+
// keep node in bounds of parent
|
|
542
|
+
if (parent) {
|
|
543
|
+
point.x = Math.min(parent.width() - model.width(), point.x);
|
|
544
|
+
point.x = Math.max(0, point.x);
|
|
545
|
+
point.y = Math.min(parent.height() - model.height(), point.y);
|
|
546
|
+
point.y = Math.max(0, point.y);
|
|
547
|
+
}
|
|
548
|
+
model.setPoint(point);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* @todo make it unit testable
|
|
552
|
+
*/
|
|
553
|
+
alignToGrid(point) {
|
|
554
|
+
const [snapX, snapY] = this.settingsService.snapGrid();
|
|
555
|
+
if (snapX > 1) {
|
|
556
|
+
point.x = align(point.x, snapX);
|
|
557
|
+
}
|
|
558
|
+
if (snapY > 1) {
|
|
559
|
+
point.y = align(point.y, snapY);
|
|
560
|
+
}
|
|
561
|
+
return point;
|
|
562
|
+
}
|
|
533
563
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DraggableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
534
564
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DraggableService }); }
|
|
535
565
|
}
|
|
536
566
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DraggableService, decorators: [{
|
|
537
567
|
type: Injectable
|
|
538
568
|
}] });
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
// keep node in bounds of parent
|
|
542
|
-
if (parent) {
|
|
543
|
-
point.x = Math.min(parent.size().width - model.size().width, point.x);
|
|
544
|
-
point.x = Math.max(0, point.x);
|
|
545
|
-
point.y = Math.min(parent.size().height - model.size().height, point.y);
|
|
546
|
-
point.y = Math.max(0, point.y);
|
|
547
|
-
}
|
|
548
|
-
model.setPoint(point);
|
|
569
|
+
function align(num, constant) {
|
|
570
|
+
return Math.ceil(num / constant) * constant;
|
|
549
571
|
}
|
|
550
572
|
|
|
551
573
|
class EdgeTemplateDirective {
|
|
@@ -1758,6 +1780,7 @@ class EdgeLabelComponent {
|
|
|
1758
1780
|
constructor() {
|
|
1759
1781
|
this.zone = inject(NgZone);
|
|
1760
1782
|
this.destroyRef = inject(DestroyRef);
|
|
1783
|
+
this.settingsService = inject(FlowSettingsService);
|
|
1761
1784
|
// TODO: too many inputs
|
|
1762
1785
|
this.model = input.required();
|
|
1763
1786
|
this.edgeModel = input.required();
|
|
@@ -1777,12 +1800,29 @@ class EdgeLabelComponent {
|
|
|
1777
1800
|
y: point.y - height / 2,
|
|
1778
1801
|
};
|
|
1779
1802
|
});
|
|
1803
|
+
this.edgeLabelStyle = computed(() => {
|
|
1804
|
+
const label = this.model().edgeLabel;
|
|
1805
|
+
if (label.type === 'default' && label.style) {
|
|
1806
|
+
const flowBackground = this.settingsService.background();
|
|
1807
|
+
let color = 'transparent';
|
|
1808
|
+
if (flowBackground.type === 'dots') {
|
|
1809
|
+
color = flowBackground.backgroundColor ?? '#fff';
|
|
1810
|
+
}
|
|
1811
|
+
if (flowBackground.type === 'solid') {
|
|
1812
|
+
color = flowBackground.color;
|
|
1813
|
+
}
|
|
1814
|
+
label.style.backgroundColor = label.style.backgroundColor ?? color;
|
|
1815
|
+
return label.style;
|
|
1816
|
+
}
|
|
1817
|
+
return null;
|
|
1818
|
+
});
|
|
1780
1819
|
}
|
|
1781
1820
|
ngAfterViewInit() {
|
|
1782
|
-
|
|
1821
|
+
const labelElement = this.edgeLabelWrapperRef().nativeElement;
|
|
1822
|
+
resizable([labelElement], this.zone)
|
|
1783
1823
|
.pipe(startWith(null), tap(() => {
|
|
1784
|
-
const width =
|
|
1785
|
-
const height =
|
|
1824
|
+
const width = labelElement.clientWidth + MAGIC_NUMBER_TO_FIX_GLITCH_IN_CHROME;
|
|
1825
|
+
const height = labelElement.clientHeight + MAGIC_NUMBER_TO_FIX_GLITCH_IN_CHROME;
|
|
1786
1826
|
this.model().size.set({ width, height });
|
|
1787
1827
|
}), takeUntilDestroyed(this.destroyRef))
|
|
1788
1828
|
.subscribe();
|
|
@@ -1796,11 +1836,11 @@ class EdgeLabelComponent {
|
|
|
1796
1836
|
};
|
|
1797
1837
|
}
|
|
1798
1838
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1799
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EdgeLabelComponent, isStandalone: true, selector: "g[edgeLabel]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, edgeModel: { classPropertyName: "edgeModel", publicName: "edgeModel", isSignal: true, isRequired: true, transformFunction: null }, point: { classPropertyName: "point", publicName: "point", isSignal: true, isRequired: false, transformFunction: null }, htmlTemplate: { classPropertyName: "htmlTemplate", publicName: "htmlTemplate", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "edgeLabelWrapperRef", first: true, predicate: ["edgeLabelWrapper"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (model().edgeLabel.type === 'html-template' && htmlTemplate()) {\n
|
|
1839
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EdgeLabelComponent, isStandalone: true, selector: "g[edgeLabel]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, edgeModel: { classPropertyName: "edgeModel", publicName: "edgeModel", isSignal: true, isRequired: true, transformFunction: null }, point: { classPropertyName: "point", publicName: "point", isSignal: true, isRequired: false, transformFunction: null }, htmlTemplate: { classPropertyName: "htmlTemplate", publicName: "htmlTemplate", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "edgeLabelWrapperRef", first: true, predicate: ["edgeLabelWrapper"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (model(); as model) {\n @if (model.edgeLabel.type === 'html-template' && htmlTemplate()) {\n @if (htmlTemplate(); as htmlTemplate) {\n <svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\" />\n </div>\n </svg:foreignObject>\n }\n }\n\n @if (model.edgeLabel.type === 'default') {\n <svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <div #edgeLabelWrapper class=\"edge-label-wrapper\" [style]=\"edgeLabelStyle()\">\n {{ model.edgeLabel.text }}\n </div>\n </svg:foreignObject>\n }\n}\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1800
1840
|
}
|
|
1801
1841
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeLabelComponent, decorators: [{
|
|
1802
1842
|
type: Component,
|
|
1803
|
-
args: [{ standalone: true, selector: 'g[edgeLabel]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], template: "@if (model().edgeLabel.type === 'html-template' && htmlTemplate()) {\n
|
|
1843
|
+
args: [{ standalone: true, selector: 'g[edgeLabel]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], template: "@if (model(); as model) {\n @if (model.edgeLabel.type === 'html-template' && htmlTemplate()) {\n @if (htmlTemplate(); as htmlTemplate) {\n <svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\" />\n </div>\n </svg:foreignObject>\n }\n }\n\n @if (model.edgeLabel.type === 'default') {\n <svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <div #edgeLabelWrapper class=\"edge-label-wrapper\" [style]=\"edgeLabelStyle()\">\n {{ model.edgeLabel.text }}\n </div>\n </svg:foreignObject>\n }\n}\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"] }]
|
|
1804
1844
|
}] });
|
|
1805
1845
|
|
|
1806
1846
|
class EdgeComponent {
|
|
@@ -3119,6 +3159,12 @@ class VflowComponent {
|
|
|
3119
3159
|
get connection() {
|
|
3120
3160
|
return this.flowEntitiesService.connection();
|
|
3121
3161
|
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Snap grid for node movement. Passes as [x, y]
|
|
3164
|
+
*/
|
|
3165
|
+
set snapGrid(value) {
|
|
3166
|
+
this.flowSettingsService.snapGrid.set(value);
|
|
3167
|
+
}
|
|
3122
3168
|
// #endregion
|
|
3123
3169
|
// #region MAIN_INPUTS
|
|
3124
3170
|
/**
|
|
@@ -3220,7 +3266,7 @@ class VflowComponent {
|
|
|
3220
3266
|
});
|
|
3221
3267
|
}
|
|
3222
3268
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3223
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: VflowComponent, isStandalone: true, selector: "vflow", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: false, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: false, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: false, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: false, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: false, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: false, isRequired: false, transformFunction: (settings) => new ConnectionModel(settings) }, nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: false, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
|
|
3269
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: VflowComponent, isStandalone: true, selector: "vflow", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: false, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: false, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: false, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: false, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: false, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: false, isRequired: false, transformFunction: (settings) => new ConnectionModel(settings) }, snapGrid: { classPropertyName: "snapGrid", publicName: "snapGrid", isSignal: false, isRequired: false, transformFunction: null }, nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: false, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
|
|
3224
3270
|
DraggableService,
|
|
3225
3271
|
ViewportService,
|
|
3226
3272
|
FlowStatusService,
|
|
@@ -3281,6 +3327,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
3281
3327
|
args: [{
|
|
3282
3328
|
transform: (settings) => new ConnectionModel(settings),
|
|
3283
3329
|
}]
|
|
3330
|
+
}], snapGrid: [{
|
|
3331
|
+
type: Input
|
|
3284
3332
|
}], nodes: [{
|
|
3285
3333
|
type: Input,
|
|
3286
3334
|
args: [{ required: true }]
|