Skip to content

Commit

Permalink
Implement node snapping (newcat#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
newcat committed May 21, 2022
1 parent a352b32 commit 4440bdf
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 17 deletions.
11 changes: 11 additions & 0 deletions docs/plugins/view.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ You can optionally enable the minimap:
viewPlugin.enableMinimap = true;
```

## Node Snapping

To snap the nodes to a custom grid, you can set the [snappingProvider](!!API%{ "module": "@baklavajs/plugin-renderer-view", "type": "class", "name": "ViewPlugin", "field": "snappingProvider" }%) property of the `ViewPlugin`. The package also exports a simple snapping implementation, which you can use like this:

```js
import { createSimpleSnappingProvider } from "@baklavajs/plugin-renderer-vue";
viewPlugin.snappingProvider = createSimpleSnappingProvider(30, 30);
```

In this case, nodes are snapped to a 30x30 grid.

## Electron
If you want to use this plugin in Electron, you might need to add it to the whitelisted externals.
To do that, add the following code to your `package.json`:
Expand Down
4 changes: 3 additions & 1 deletion packages/baklavajs-playground/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Component, Vue } from "vue-property-decorator";
import { VueConstructor } from "vue";
import { Editor, Node, NodeInterface } from "../../baklavajs-core/src";
import { ViewPlugin } from "../../baklavajs-plugin-renderer-vue/src";
import { ViewPlugin, createSimpleSnappingProvider } from "../../baklavajs-plugin-renderer-vue/src";
import { Engine } from "../../baklavajs-plugin-engine/src";
import { InterfaceTypePlugin } from "../../baklavajs-plugin-interface-types/src";
import { OptionPlugin } from "../../baklavajs-plugin-options-vue/src";
Expand Down Expand Up @@ -81,6 +81,8 @@ export default class App extends Vue {
this.viewPlugin.registerOption("AddOption", AddOption);
this.viewPlugin.registerOption("TriggerOption", TriggerOption);
this.viewPlugin.registerOption("SidebarOption", SidebarOption);
this.viewPlugin.snappingProvider = createSimpleSnappingProvider(30, 30);
}
mounted() {
Expand Down
32 changes: 17 additions & 15 deletions packages/baklavajs-plugin-renderer-vue/src/components/node/Node.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
to="sidebar"
v-if="
plugin.sidebar.nodeId === data.id &&
plugin.sidebar.optionName === name &&
option.sidebarComponent
plugin.sidebar.optionName === name &&
option.sidebarComponent
"
>
<component
Expand Down Expand Up @@ -99,8 +99,8 @@ interface IPosition {
@Component({
directives: {
ClickOutside: ClickOutside.directive,
},
ClickOutside: ClickOutside.directive
}
})
export default class NodeView extends Vue {
@Prop({ type: Object })
Expand All @@ -126,8 +126,8 @@ export default class NodeView extends Vue {
y: 0,
items: [
{ value: "rename", label: "Rename" },
{ value: "delete", label: "Delete" },
],
{ value: "delete", label: "Delete" }
]
};
get classes() {
Expand All @@ -137,15 +137,15 @@ export default class NodeView extends Vue {
"--dragging": !!this.draggingStartPoint,
"--two-column": !!this.data.twoColumn,
[`--type-${sanitizeName(this.data.type)}`]: true,
[this.data.customClasses]: true,
[this.data.customClasses]: true
};
}
get styles() {
return {
top: `${this.data.position.y}px`,
left: `${this.data.position.x}px`,
width: `${this.data.width}px`,
width: `${this.data.width}px`
};
}
Expand Down Expand Up @@ -182,12 +182,12 @@ export default class NodeView extends Vue {
this.selectedNodeViews.forEach((elem) => {
elem.draggingStartPoint = {
x: ev.screenX,
y: ev.screenY,
x: ev.screenX,
y: ev.screenY
};
elem.draggingStartPosition = {
x: elem.data.position.x,
y: elem.data.position.y,
x: elem.data.position.x,
y: elem.data.position.y
};
document.addEventListener("mousemove", elem.handleMove);
document.addEventListener("mouseup", elem.stopDrag);
Expand All @@ -211,9 +211,11 @@ export default class NodeView extends Vue {
if (this.draggingStartPoint) {
const dx = ev.screenX - this.draggingStartPoint.x;
const dy = ev.screenY - this.draggingStartPoint.y;
const newX = this.draggingStartPosition!.x + dx / this.plugin.scaling;
const newY = this.draggingStartPosition!.y + dy / this.plugin.scaling;
const { x: newX, y: newY } = this.plugin.snappingProvider(
this.draggingStartPosition!.x + dx / this.plugin.scaling,
this.draggingStartPosition!.y + dy / this.plugin.scaling
);
const eventData = {
nodeView: this,
newPosition: { x: newX, y: newY }
Expand Down
1 change: 1 addition & 0 deletions packages/baklavajs-plugin-renderer-vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export const Components = {
};

export * from "./baklavaVuePlugin";
export * from "./snapping";
export * from "./viewPlugin";
9 changes: 9 additions & 0 deletions packages/baklavajs-plugin-renderer-vue/src/snapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/** A function that receives raw x and y values and returns the new x and y values, which the node should be snapped to */
export type SnappingProvider = (x: number, y: number) => { x: number; y: number };

export function createSimpleSnappingProvider(xGrid: number, yGrid: number): SnappingProvider {
return (x, y) => ({
x: Math.round(x / xGrid) * xGrid,
y: Math.round(y / yGrid) * yGrid
});
}
9 changes: 8 additions & 1 deletion packages/baklavajs-plugin-renderer-vue/src/viewPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IPlugin, IEditor } from "../../baklavajs-core/types";
import { BaklavaEvent, PreventableBaklavaEvent, SequentialHook } from "@baklavajs/events";
import { IViewNode, IViewPlugin } from "../types";
import { NodeMoveEventData } from "./eventTypes";
import { createSimpleSnappingProvider, SnappingProvider } from "./snapping";

import NodeView from "./components/node/Node.vue";
import NodeOptionView from "./components/node/NodeOption.vue";
Expand All @@ -29,12 +30,18 @@ export class ViewPlugin implements IPlugin, IViewPlugin {
public enableMinimap = false;

/** Background configuration */
backgroundGrid = {
public backgroundGrid = {
gridSize: 100,
gridDivision: 5,
subGridVisibleThreshold: 0.6
};

/**
* Set this property to your own SnappingProvider to implement custom snapping logic.
* You can also use the "createSimpleSnappingProvider" with custom xGrid and yGrid values.
*/
public snappingProvider: SnappingProvider = createSimpleSnappingProvider(1, 1);

public options: Record<string, VueConstructor> = {};
public nodeTypeAliases: Record<string, string> = {};

Expand Down

0 comments on commit 4440bdf

Please sign in to comment.