@stonecrop/node-editor 0.2.47 → 0.2.48

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/dist/style.css CHANGED
@@ -1 +1 @@
1
- .editable-edge-label{background-color:#fff;position:relative;font-size:12px}.label-input-wrapper{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center}.label-input{text-align:center}.vue-flow{position:relative;width:100%;height:100%;overflow:hidden;z-index:0;direction:ltr}.vue-flow__container{position:absolute;height:100%;width:100%;left:0;top:0}.vue-flow__pane{z-index:1}.vue-flow__pane.draggable{cursor:grab}.vue-flow__pane.dragging{cursor:grabbing}.vue-flow__pane.selection{cursor:pointer}.vue-flow__transformationpane{transform-origin:0 0;z-index:2;pointer-events:none}.vue-flow__viewport{z-index:4;overflow:clip}.vue-flow__selection{z-index:6}.vue-flow__edge-labels{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.vue-flow__nodesselection-rect:focus,.vue-flow__nodesselection-rect:focus-visible{outline:none}.vue-flow .vue-flow__edges{pointer-events:none;overflow:visible}.vue-flow__edge-path,.vue-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.vue-flow__edge{pointer-events:visibleStroke;cursor:pointer}.vue-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.vue-flow__edge.animated path.vue-flow__edge-interaction{stroke-dasharray:none;animation:none}.vue-flow__edge.inactive{pointer-events:none}.vue-flow__edge.selected,.vue-flow__edge:focus,.vue-flow__edge:focus-visible{outline:none}.vue-flow__edge.selected .vue-flow__edge-path,.vue-flow__edge:focus .vue-flow__edge-path,.vue-flow__edge:focus-visible .vue-flow__edge-path{stroke:#555}.vue-flow__edge-textwrapper{pointer-events:all}.vue-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.vue-flow__connection{pointer-events:none}.vue-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.vue-flow__connectionline{z-index:1001}.vue-flow__nodes{pointer-events:none;transform-origin:0 0}.vue-flow__node-default,.vue-flow__node-input,.vue-flow__node-output{border-width:1px;border-style:solid;border-color:#bbb}.vue-flow__node-default.selected,.vue-flow__node-default:focus,.vue-flow__node-default:focus-visible,.vue-flow__node-input.selected,.vue-flow__node-input:focus,.vue-flow__node-input:focus-visible,.vue-flow__node-output.selected,.vue-flow__node-output:focus,.vue-flow__node-output:focus-visible{outline:none;border:1px solid #555}.vue-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.vue-flow__node.draggable{cursor:grab;pointer-events:all}.vue-flow__node.draggable.dragging{cursor:grabbing}.vue-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.vue-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.vue-flow__nodesselection-rect.dragging{cursor:grabbing}.vue-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px}.vue-flow__handle.connectable{pointer-events:all;cursor:crosshair}.vue-flow__handle-bottom{left:50%;bottom:0;transform:translate(-50%,50%)}.vue-flow__handle-top{left:50%;top:0;transform:translate(-50%,-50%)}.vue-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.vue-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.vue-flow__edgeupdater{cursor:move;pointer-events:all}.vue-flow__panel{position:absolute;z-index:5;margin:15px}.vue-flow__panel.top{top:0}.vue-flow__panel.bottom{bottom:0}.vue-flow__panel.left{left:0}.vue-flow__panel.right{right:0}.vue-flow__panel.center{left:50%;transform:translate(-50%)}@keyframes dashdraw{0%{stroke-dashoffset:10}}:root{--vf-node-bg: #fff;--vf-node-text: #222;--vf-connection-path: #b1b1b7;--vf-handle: #555}.vue-flow__edge.updating .vue-flow__edge-path{stroke:#777}.vue-flow__edge-text{font-size:10px}.vue-flow__edge-textbg{fill:#fff}.vue-flow__connection-path{stroke:var(--vf-connection-path)}.vue-flow__node{cursor:grab}.vue-flow__node.selectable:focus,.vue-flow__node.selectable:focus-visible{outline:none}.vue-flow__node-default,.vue-flow__node-input,.vue-flow__node-output{padding:10px;border-radius:3px;width:150px;font-size:12px;text-align:center;border-width:1px;border-style:solid;color:var(--vf-node-text);background-color:var(--vf-node-bg);border-color:var(--vf-node-color)}.vue-flow__node-default.selected,.vue-flow__node-default.selected:hover,.vue-flow__node-input.selected,.vue-flow__node-input.selected:hover,.vue-flow__node-output.selected,.vue-flow__node-output.selected:hover{box-shadow:0 0 0 .5px var(--vf-box-shadow)}.vue-flow__node-default .vue-flow__handle,.vue-flow__node-input .vue-flow__handle,.vue-flow__node-output .vue-flow__handle{background:var(--vf-handle)}.vue-flow__node-default.selectable:hover,.vue-flow__node-input.selectable:hover,.vue-flow__node-output.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.vue-flow__node-input{--vf-node-color: var(--vf-node-color, #0041d0);--vf-handle: var(--vf-node-color, #0041d0);--vf-box-shadow: var(--vf-node-color, #0041d0);background:var(--vf-node-bg);border-color:var(--vf-node-color, #0041d0)}.vue-flow__node-input.selected,.vue-flow__node-input:focus,.vue-flow__node-input:focus-visible{outline:none;border:1px solid var(--vf-node-color, #0041d0)}.vue-flow__node-default{--vf-handle: var(--vf-node-color, #1a192b);--vf-box-shadow: var(--vf-node-color, #1a192b);background:var(--vf-node-bg);border-color:var(--vf-node-color, #1a192b)}.vue-flow__node-default.selected,.vue-flow__node-default:focus,.vue-flow__node-default:focus-visible{outline:none;border:1px solid var(--vf-node-color, #1a192b)}.vue-flow__node-output{--vf-handle: var(--vf-node-color, #ff0072);--vf-box-shadow: var(--vf-node-color, #ff0072);background:var(--vf-node-bg);border-color:var(--vf-node-color, #ff0072)}.vue-flow__node-output.selected,.vue-flow__node-output:focus,.vue-flow__node-output:focus-visible{outline:none;border:1px solid var(--vf-node-color, #ff0072)}.vue-flow__nodesselection-rect,.vue-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.vue-flow__nodesselection-rect:focus,.vue-flow__nodesselection-rect:focus-visible,.vue-flow__selection:focus,.vue-flow__selection:focus-visible{outline:none}.vue-flow__handle{width:6px;height:6px;background:var(--vf-handle);border:1px solid #fff;border-radius:100%}.chart-controls-left,.chart-controls-right{height:1.8em;display:flex;flex-direction:row;align-items:center;padding-top:.2em}.chart-controls-right div{margin-left:5px}.chart-controls{padding-left:20px;padding-right:20px;height:1.8em;border-bottom:1px solid #ccc;display:flex;flex-direction:row;justify-content:space-between}.chart-controls div{margin-bottom:5px}.defaultContainerClass{height:90vh;width:100%;border:1px solid #ccc}.default-input-node.vue-flow__node-input,.default-output-node.vue-flow__node-output{border-color:#000}.default-input-node.vue-flow__node-input .vue-flow__handle,.default-output-node.vue-flow__node-output .vue-flow__handle{background-color:#000}.default-input-node.vue-flow__node-input.selected,.default-output-node.vue-flow__node-output.selected{box-shadow:0 0 0 .5px #000}button.button-default{background-color:#fff;padding:1px 12px;border-radius:3px;border:1px solid #ccc;cursor:pointer;white-space:nowrap}button.button-default:hover{background-color:#f2f2f2}.vue-flow{background-size:40px 40px;background-image:linear-gradient(to right,#ccc 1px,transparent 1px),linear-gradient(to bottom,#ccc 1px,transparent 1px)}input.label-editor{position:absolute}.node-editor-wrapper{position:relative}
1
+ .editable-edge-label{background-color:#fff;position:relative;font-size:12px}.label-input{text-align:center;width:40ch}.label-input-wrapper{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center}.label-input{text-align:center}.vue-flow{position:relative;width:100%;height:100%;overflow:hidden;z-index:0;direction:ltr}.vue-flow__container{position:absolute;height:100%;width:100%;left:0;top:0}.vue-flow__pane{z-index:1}.vue-flow__pane.draggable{cursor:grab}.vue-flow__pane.dragging{cursor:grabbing}.vue-flow__pane.selection{cursor:pointer}.vue-flow__transformationpane{transform-origin:0 0;z-index:2;pointer-events:none}.vue-flow__viewport{z-index:4;overflow:clip}.vue-flow__selection{z-index:6}.vue-flow__edge-labels{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.vue-flow__nodesselection-rect:focus,.vue-flow__nodesselection-rect:focus-visible{outline:none}.vue-flow .vue-flow__edges{pointer-events:none;overflow:visible}.vue-flow__edge-path,.vue-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.vue-flow__edge{pointer-events:visibleStroke;cursor:pointer}.vue-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.vue-flow__edge.animated path.vue-flow__edge-interaction{stroke-dasharray:none;animation:none}.vue-flow__edge.inactive{pointer-events:none}.vue-flow__edge.selected,.vue-flow__edge:focus,.vue-flow__edge:focus-visible{outline:none}.vue-flow__edge.selected .vue-flow__edge-path,.vue-flow__edge:focus .vue-flow__edge-path,.vue-flow__edge:focus-visible .vue-flow__edge-path{stroke:#555}.vue-flow__edge-textwrapper{pointer-events:all}.vue-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.vue-flow__connection{pointer-events:none}.vue-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.vue-flow__connectionline{z-index:1001}.vue-flow__nodes{pointer-events:none;transform-origin:0 0}.vue-flow__node-default,.vue-flow__node-input,.vue-flow__node-output{border-width:1px;border-style:solid;border-color:#bbb}.vue-flow__node-default.selected,.vue-flow__node-default:focus,.vue-flow__node-default:focus-visible,.vue-flow__node-input.selected,.vue-flow__node-input:focus,.vue-flow__node-input:focus-visible,.vue-flow__node-output.selected,.vue-flow__node-output:focus,.vue-flow__node-output:focus-visible{outline:none;border:1px solid #555}.vue-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.vue-flow__node.draggable{cursor:grab;pointer-events:all}.vue-flow__node.draggable.dragging{cursor:grabbing}.vue-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.vue-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.vue-flow__nodesselection-rect.dragging{cursor:grabbing}.vue-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px}.vue-flow__handle.connectable{pointer-events:all;cursor:crosshair}.vue-flow__handle-bottom{left:50%;bottom:0;transform:translate(-50%,50%)}.vue-flow__handle-top{left:50%;top:0;transform:translate(-50%,-50%)}.vue-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.vue-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.vue-flow__edgeupdater{cursor:move;pointer-events:all}.vue-flow__panel{position:absolute;z-index:5;margin:15px}.vue-flow__panel.top{top:0}.vue-flow__panel.bottom{bottom:0}.vue-flow__panel.left{left:0}.vue-flow__panel.right{right:0}.vue-flow__panel.center{left:50%;transform:translate(-50%)}@keyframes dashdraw{0%{stroke-dashoffset:10}}:root{--vf-node-bg: #fff;--vf-node-text: #222;--vf-connection-path: #b1b1b7;--vf-handle: #555}.vue-flow__edge.updating .vue-flow__edge-path{stroke:#777}.vue-flow__edge-text{font-size:10px}.vue-flow__edge-textbg{fill:#fff}.vue-flow__connection-path{stroke:var(--vf-connection-path)}.vue-flow__node{cursor:grab}.vue-flow__node.selectable:focus,.vue-flow__node.selectable:focus-visible{outline:none}.vue-flow__node-default,.vue-flow__node-input,.vue-flow__node-output{padding:10px;border-radius:3px;width:150px;font-size:12px;text-align:center;border-width:1px;border-style:solid;color:var(--vf-node-text);background-color:var(--vf-node-bg);border-color:var(--vf-node-color)}.vue-flow__node-default.selected,.vue-flow__node-default.selected:hover,.vue-flow__node-input.selected,.vue-flow__node-input.selected:hover,.vue-flow__node-output.selected,.vue-flow__node-output.selected:hover{box-shadow:0 0 0 .5px var(--vf-box-shadow)}.vue-flow__node-default .vue-flow__handle,.vue-flow__node-input .vue-flow__handle,.vue-flow__node-output .vue-flow__handle{background:var(--vf-handle)}.vue-flow__node-default.selectable:hover,.vue-flow__node-input.selectable:hover,.vue-flow__node-output.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.vue-flow__node-input{--vf-node-color: var(--vf-node-color, #0041d0);--vf-handle: var(--vf-node-color, #0041d0);--vf-box-shadow: var(--vf-node-color, #0041d0);background:var(--vf-node-bg);border-color:var(--vf-node-color, #0041d0)}.vue-flow__node-input.selected,.vue-flow__node-input:focus,.vue-flow__node-input:focus-visible{outline:none;border:1px solid var(--vf-node-color, #0041d0)}.vue-flow__node-default{--vf-handle: var(--vf-node-color, #1a192b);--vf-box-shadow: var(--vf-node-color, #1a192b);background:var(--vf-node-bg);border-color:var(--vf-node-color, #1a192b)}.vue-flow__node-default.selected,.vue-flow__node-default:focus,.vue-flow__node-default:focus-visible{outline:none;border:1px solid var(--vf-node-color, #1a192b)}.vue-flow__node-output{--vf-handle: var(--vf-node-color, #ff0072);--vf-box-shadow: var(--vf-node-color, #ff0072);background:var(--vf-node-bg);border-color:var(--vf-node-color, #ff0072)}.vue-flow__node-output.selected,.vue-flow__node-output:focus,.vue-flow__node-output:focus-visible{outline:none;border:1px solid var(--vf-node-color, #ff0072)}.vue-flow__nodesselection-rect,.vue-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.vue-flow__nodesselection-rect:focus,.vue-flow__nodesselection-rect:focus-visible,.vue-flow__selection:focus,.vue-flow__selection:focus-visible{outline:none}.vue-flow__handle{width:6px;height:6px;background:var(--vf-handle);border:1px solid #fff;border-radius:100%}.chart-controls-left,.chart-controls-right{height:1.8em;display:flex;flex-direction:row;align-items:center;padding-top:.2em}.chart-controls-right div{margin-left:5px}.chart-controls{padding-left:20px;padding-right:20px;height:1.8em;border-bottom:1px solid #ccc;display:flex;flex-direction:row;justify-content:space-between}.chart-controls div{margin-bottom:5px}.defaultContainerClass{height:90vh;width:100%;border:1px solid #ccc}.default-input-node.vue-flow__node-input,.default-output-node.vue-flow__node-output{border-color:#000}.default-input-node.vue-flow__node-input .vue-flow__handle,.default-output-node.vue-flow__node-output .vue-flow__handle{background-color:#000}.default-input-node.vue-flow__node-input.selected,.default-output-node.vue-flow__node-output.selected{box-shadow:0 0 0 .5px #000}button.button-default{background-color:#fff;padding:1px 12px;border-radius:3px;border:1px solid #ccc;cursor:pointer;white-space:nowrap}button.button-default:hover{background-color:#f2f2f2}.vue-flow{background-size:40px 40px;background-image:linear-gradient(to right,#ccc 1px,transparent 1px),linear-gradient(to bottom,#ccc 1px,transparent 1px)}input.label-editor{position:absolute}.node-editor-wrapper{position:relative}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stonecrop/node-editor",
3
- "version": "0.2.47",
3
+ "version": "0.2.48",
4
4
  "description": "Node editor UI for Stonecrop",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -87,5 +87,6 @@ export default {
87
87
 
88
88
  .label-input {
89
89
  text-align: center;
90
+ width: 40ch;
90
91
  }
91
92
  </style>
@@ -39,7 +39,7 @@
39
39
  </template>
40
40
 
41
41
  <script setup lang="ts">
42
- import { type VueFlowStore, Position, VueFlow, useVueFlow, Node } from '@vue-flow/core'
42
+ import { type VueFlowStore, Position, VueFlow, useVueFlow, Connection, Node } from '@vue-flow/core'
43
43
  import { type HTMLAttributes, ref, computed, defineEmits, onBeforeUnmount, onMounted } from 'vue'
44
44
 
45
45
  import EditableEdge from '@/components/EditableEdge.vue'
@@ -103,6 +103,7 @@ const elements = computed({
103
103
  emit('update:modelValue', JSON.parse(JSON.stringify(newValue)))
104
104
  },
105
105
  })
106
+ const { onConnect, addEdges, onEdgeContextMenu, onPaneReady, removeEdges } = useVueFlow()
106
107
 
107
108
  onMounted(() => {
108
109
  document.removeEventListener('keypress', handleKeypress)
@@ -113,7 +114,6 @@ onBeforeUnmount(() => {
113
114
  document.removeEventListener('keypress', handleKeypress)
114
115
  })
115
116
 
116
- const { onPaneReady } = useVueFlow()
117
117
  onPaneReady(instance => {
118
118
  vueFlowInstance.value = instance
119
119
  })
@@ -222,6 +222,35 @@ const labelChanged = (e, id) => {
222
222
  }
223
223
  }
224
224
  }
225
+
226
+ const handleConnect = (connection: Connection) => {
227
+ const id = vueFlowElements.value.length
228
+ const newEdge = {
229
+ id: `edge-${id}`,
230
+ source: connection.source,
231
+ target: connection.target,
232
+ type: 'editable',
233
+ label: `New Edge`,
234
+ interactionWidth: 400,
235
+ animated: true,
236
+ events: {
237
+ click: () => {
238
+ activeElementKey.value = newEdge.id
239
+ },
240
+ },
241
+ }
242
+ addEdges([newEdge])
243
+ }
244
+
245
+ onConnect(handleConnect)
246
+
247
+ const handleEdgeRemove = edgeId => {
248
+ removeEdges(edgeId)
249
+ }
250
+
251
+ onEdgeContextMenu(({ event, edge }) => {
252
+ handleEdgeRemove(edge.id)
253
+ })
225
254
  </script>
226
255
 
227
256
  <style>
@@ -40,31 +40,28 @@ const elements = computed<FlowElements>({
40
40
  }
41
41
 
42
42
  stateHash[key] = el
43
+ }
43
44
 
45
+ for (const [key, value] of Object.entries(states.value)) {
44
46
  if (value.on) {
45
47
  for (const [edgeKey, edgeValue] of Object.entries(value.on)) {
46
- if (Array.isArray(edgeValue)) {
47
- for (const edge of edgeValue) {
48
- // TODO: handle typescript errors for both types of states
49
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
50
- const edgeJson = edge.toJSON()
51
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
52
- const target = edgeJson.target.toString()
53
- stateElements.push({
54
- id: `${key}-${edgeKey}`,
55
- target: target,
56
- source: key,
57
- label: edgeKey,
58
- animated: true,
59
- })
60
-
61
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
62
- hasInputs[target] = true
63
- }
64
- }
48
+ // If the proxy array 'value.on' has more than one edge, 'edgeValue' will contain a proxy object
49
+ // where 'target' can be accessed. Otherwise, 'edgeValue' will be available directly.
50
+ const target = edgeValue.target || edgeValue
51
+ // TODO: handle typescript errors for both types of states
52
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
53
+ stateElements.push({
54
+ id: `${key}-${target}`,
55
+ source: key,
56
+ target: target,
57
+ label: edgeKey,
58
+ animated: true,
59
+ })
60
+
61
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
62
+ hasInputs[target] = true
65
63
  }
66
64
  }
67
-
68
65
  index++
69
66
  }
70
67
 
@@ -78,6 +75,7 @@ const elements = computed<FlowElements>({
78
75
 
79
76
  return stateElements
80
77
  },
78
+
81
79
  set: newValue => {
82
80
  // update modelValue when elements change
83
81
  onElementsChange(newValue)
@@ -85,7 +83,6 @@ const elements = computed<FlowElements>({
85
83
  // TODO: emit('update:modelValue', modelValue)
86
84
  },
87
85
  })
88
-
89
86
  const onElementsChange = (elements: FlowElements) => {
90
87
  const edges: Record<string, Record<string, any>> = {}
91
88
  const idToLabel: Record<string, string> = {}
@@ -104,19 +101,18 @@ const onElementsChange = (elements: FlowElements) => {
104
101
  states[label] = {
105
102
  type: 'final',
106
103
  }
107
- } /* else if (el.source && el.target) {
104
+ } else if (el.source && el.target) {
108
105
  // it's an edge
109
106
  edges[el.source] = edges[el.source] || {}
110
107
  edges[el.source][label] = {
111
108
  target: el.target,
112
109
  }
113
- } */ else {
110
+ } else {
114
111
  // it's a state
115
112
  states[label] = {
116
113
  on: {},
117
114
  }
118
115
  }
119
-
120
116
  idToLabel[el.id] = label
121
117
  }
122
118