Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Patrik Meijer
react-components
Commits
c281d62c
Commit
c281d62c
authored
Feb 20, 2019
by
Patrik Meijer
Browse files
Can create Connections and Pointers (and almost sets members) in GraphEditor
parent
a4ef4759
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/components/GraphEditor/ContextMenu.jsx
deleted
100644 → 0
View file @
a4ef4759
/* globals document */
/**
* TODO: Consider moving this outside of the GraphEditor and let it be passed as child instead.
* @author pmeijer / https://github.com/pmeijer
*/
import
React
,
{
Component
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
Menu
from
'
@material-ui/core/Menu
'
;
import
MenuItem
from
'
@material-ui/core/MenuItem
'
;
export
default
class
ContextMenu
extends
Component
{
static
propTypes
=
{
gmeClient
:
PropTypes
.
object
.
isRequired
,
nodeId
:
PropTypes
.
string
.
isRequired
,
// FIXME: should be nodeIds
eventX
:
PropTypes
.
number
.
isRequired
,
eventY
:
PropTypes
.
number
.
isRequired
,
onClose
:
PropTypes
.
func
.
isRequired
,
setActiveNode
:
PropTypes
.
func
.
isRequired
,
createPointer
:
PropTypes
.
func
.
isRequired
,
readOnly
:
PropTypes
.
bool
,
};
static
defaultProps
=
{
readOnly
:
false
,
}
setActiveNode
=
()
=>
{
const
{
nodeId
,
onClose
,
setActiveNode
}
=
this
.
props
;
setActiveNode
(
nodeId
);
onClose
();
}
deleteNode
=
()
=>
{
const
{
gmeClient
,
nodeId
,
onClose
}
=
this
.
props
;
gmeClient
.
deleteNode
(
nodeId
);
onClose
();
}
getCreatePointerFunc
=
(
ptrName
)
=>
{
const
{
createPointer
,
nodeId
}
=
this
.
props
;
return
()
=>
{
createPointer
(
nodeId
,
ptrName
);
};
}
render
()
{
const
{
gmeClient
,
nodeId
,
eventX
,
eventY
,
readOnly
,
onClose
,
}
=
this
.
props
;
// console.log(data);
const
menuItems
=
[];
const
nodeObj
=
gmeClient
.
getNode
(
nodeId
);
const
metaNodeObj
=
gmeClient
.
getNode
(
nodeObj
.
getMetaTypeId
());
const
pointerNames
=
metaNodeObj
.
getValidPointerNames
();
menuItems
.
push
((
<
MenuItem
key
=
"meta-type"
onClick
=
{
onClose
}
disabled
=
{
readOnly
}
>
{
`<<
${
metaNodeObj
.
getAttribute
(
'
name
'
)}
>>`
}
</
MenuItem
>));
menuItems
.
push
((
<
MenuItem
key
=
"open-sub-system"
onClick
=
{
this
.
setActiveNode
}
>
Open Subsystem ...
</
MenuItem
>));
pointerNames
.
forEach
((
ptr
)
=>
{
menuItems
.
push
((
<
MenuItem
key
=
{
`create-pointer-
${
ptr
}
`
}
onClick
=
{
this
.
getCreatePointerFunc
(
ptr
)
}
>
{
`Create pointer
${
ptr
}
from here ...`
}
</
MenuItem
>));
});
return
(
<
Menu
id
=
"simple-menu"
anchorEl
=
{
document
.
body
}
style
=
{
{
position
:
'
absolute
'
,
top
:
eventY
-
(
document
.
body
.
clientHeight
/
2
),
left
:
eventX
,
}
}
open
onClose
=
{
onClose
}
>
{
menuItems
}
</
Menu
>
);
}
}
src/components/GraphEditor/DefaultStyles.jsx
View file @
c281d62c
...
...
@@ -29,9 +29,10 @@ const DEFAULT_STYLES = [
},
{
selector
:
'
node.aux-node
'
,
css
:
{
style
:
{
width
:
6
,
height
:
6
,
'
background-color
'
:
'
rgb(174, 219, 255)
'
,
},
},
{
...
...
@@ -89,9 +90,15 @@ const DEFAULT_STYLES = [
},
},
{
selector
:
'
node.valid-pointer-target
'
,
selector
:
'
node.valid-action-target
'
,
style
:
{
'
background-color
'
:
'
rgba(185, 236, 185, 0.64)
'
,
},
},
{
selector
:
'
node.invalid-action-target
'
,
style
:
{
'
background-color
'
:
'
rgb
(6
6,
22
0,
24
4)
'
,
'
background-color
'
:
'
rgb
a(23
6,
15
0,
185, 0.6
4)
'
,
},
},
{
...
...
src/components/GraphEditor/GraphEditor.jsx
View file @
c281d62c
...
...
@@ -19,7 +19,7 @@ import FilterIcon from '@material-ui/icons/FilterList';
import
ViewModuleIcon
from
'
@material-ui/icons/ViewModule
'
;
import
ReactCytoscape
from
'
./ReactCytoscape
'
;
import
Context
Menu
from
'
./
Context
Menu
'
;
import
SimpleAction
Menu
from
'
.
.
/
SimpleAction
Menu
'
;
import
FilterSelector
from
'
./FilterSelector
'
;
import
DEFAULT_STYLES
from
'
./DefaultStyles
'
;
...
...
@@ -138,7 +138,7 @@ export default class GraphEditor extends Component {
return
res
;
})(),
showFilterSelector
:
false
,
creat
ePointer
:
null
,
creat
ingNew
:
null
,
};
componentWillReceiveProps
({
activeNode
})
{
...
...
@@ -167,7 +167,7 @@ export default class GraphEditor extends Component {
activeSelection
,
nodes
,
}
=
this
.
props
;
const
{
activeFilters
,
creat
ePointer
}
=
this
.
state
;
const
{
activeFilters
,
creat
ingNew
}
=
this
.
state
;
// https://github.com/ybarukh/react-cytoscape/blob/master/sample/app/Graph.js
// http://js.cytoscape.org/#notation/elements-json
const
result
=
{
...
...
@@ -241,8 +241,8 @@ export default class GraphEditor extends Component {
${
activeSelection
.
includes
(
id
)
?
'
in-active-selection
'
:
''
}
`
,
};
if
(
creat
ePointer
&&
creat
ePointer
.
target
===
id
)
{
edgeData
.
classes
+=
'
valid-pointer
-target
'
;
if
(
creat
ingNew
&&
creat
ingNew
.
target
===
id
)
{
edgeData
.
classes
+=
creatingNew
.
isValid
?
'
valid-action-target
'
:
'
invalid-action
-target
'
;
}
if
(
hasEdgeTargets
(
childData
.
pointers
.
src
,
childData
.
pointers
.
dst
))
{
...
...
@@ -285,8 +285,8 @@ ${activeSelection.includes(id) ? 'in-active-selection' : ''}`,
});
}
if
(
creat
ePointer
&&
creat
ePointer
.
target
===
id
)
{
cytoData
.
classes
+=
'
valid-pointer
-target
'
;
if
(
creat
ingNew
&&
creat
ingNew
.
target
===
id
)
{
cytoData
.
classes
+=
creatingNew
.
isValid
?
'
valid-action-target
'
:
'
invalid-action
-target
'
;
}
}
...
...
@@ -414,10 +414,25 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
startNewPointer
=
(
nodeId
,
ptrName
)
=>
{
this
.
setState
({
showNodeMenu
:
false
,
createPointer
:
{
creatingNew
:
{
type
:
'
pointer
'
,
nodeId
,
ptrName
,
id
:
ptrName
,
target
:
null
,
isValid
:
null
,
},
});
};
startNewConnection
=
(
nodeId
,
connTypeId
)
=>
{
this
.
setState
({
showNodeMenu
:
false
,
creatingNew
:
{
type
:
'
connection
'
,
nodeId
,
id
:
connTypeId
,
target
:
null
,
isValid
:
null
,
},
});
};
...
...
@@ -459,22 +474,50 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
this
.
cy
.
on
(
'
vclick
'
,
'
node
'
,
(
e
)
=>
{
const
{
setActiveSelection
,
gmeClient
}
=
this
.
props
;
const
{
createPointer
}
=
this
.
state
;
const
{
creatingNew
}
=
this
.
state
;
// This is async :+1:
this
.
setState
({
creatingNew
:
null
});
if
(
typeof
e
.
target
.
id
===
'
function
'
)
{
if
(
createPointer
&&
createPointer
.
target
===
e
.
target
.
id
())
{
this
.
setState
({
createPointer
:
null
});
gmeClient
.
setPointer
(
createPointer
.
nodeId
,
createPointer
.
ptrName
,
createPointer
.
target
);
const
nodeId
=
e
.
target
.
id
();
if
(
creatingNew
&&
creatingNew
.
target
===
nodeId
&&
creatingNew
.
isValid
)
{
if
(
creatingNew
.
type
===
'
pointer
'
)
{
gmeClient
.
setPointer
(
creatingNew
.
nodeId
,
creatingNew
.
id
,
creatingNew
.
target
);
}
else
if
(
creatingNew
.
type
===
'
set
'
)
{
// FIXME: This uses the shuffled client arguments for addMember!
gmeClient
.
addMember
(
creatingNew
.
nodeId
,
creatingNew
.
target
,
creatingNew
.
id
);
}
else
if
(
creatingNew
.
type
===
'
connection
'
)
{
const
gmeNode
=
gmeClient
.
getNode
(
nodeId
);
if
(
gmeNode
)
{
const
commonParentId
=
gmeNode
.
getCommonParentId
(
creatingNew
.
nodeId
);
gmeClient
.
startTransaction
();
const
connId
=
gmeClient
.
createNode
({
baseId
:
creatingNew
.
id
,
parentId
:
commonParentId
,
});
gmeClient
.
setPointer
(
connId
,
'
src
'
,
creatingNew
.
nodeId
);
gmeClient
.
setPointer
(
connId
,
'
dst
'
,
creatingNew
.
target
);
gmeClient
.
completeTransaction
();
setTimeout
(()
=>
{
setActiveSelection
([
connId
]);
});
}
}
else
{
console
.
error
(
'
Unexpected createNew type
'
,
JSON
.
stringy
(
creatingNew
));
}
}
else
if
(
e
.
target
.
hasClass
(
'
aux-node
'
)
===
false
)
{
this
.
setState
({
showNodeMenu
:
{
id
:
e
.
target
.
id
(),
data
:
e
.
target
.
data
(),
position
:
{
x
:
e
.
originalEvent
.
clientX
,
y
:
e
.
originalEvent
.
clientY
,
},
},
createPointer
:
null
,
});
setTimeout
(()
=>
{
...
...
@@ -487,22 +530,34 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
});
this
.
cy
.
on
(
'
mouseover
'
,
'
node
'
,
(
e
)
=>
{
const
{
creat
ePointer
}
=
this
.
state
;
const
{
creat
ingNew
}
=
this
.
state
;
const
{
gmeClient
}
=
this
.
props
;
if
(
createPointer
&&
typeof
e
.
target
.
id
===
'
function
'
&&
e
.
target
.
hasClass
(
'
aux-node
'
)
===
false
)
{
const
gmeNode
=
gmeClient
.
getNode
(
e
.
target
.
id
());
if
(
gmeNode
&&
gmeNode
.
isValidTargetOf
(
createPointer
.
nodeId
,
createPointer
.
ptrName
)
&&
createPointer
.
target
!==
e
.
target
.
id
())
{
this
.
setState
({
createPointer
:
{
nodeId
:
createPointer
.
nodeId
,
ptrName
:
createPointer
.
ptrName
,
target
:
e
.
target
.
id
(),
},
});
const
nodeId
=
e
.
target
.
id
();
const
gmeNode
=
gmeClient
.
getNode
(
nodeId
);
if
(
!
gmeNode
)
{
return
;
}
if
(
creatingNew
&&
creatingNew
.
target
!==
nodeId
)
{
let
isValid
=
false
;
if
(
creatingNew
.
type
===
'
pointer
'
)
{
isValid
=
gmeNode
.
isValidTargetOf
(
creatingNew
.
nodeId
,
creatingNew
.
id
);
}
else
if
(
creatingNew
.
type
===
'
set
'
)
{
isValid
=
gmeNode
.
isValidMemberOf
(
creatingNew
.
nodeId
,
creatingNew
.
id
);
}
else
if
(
creatingNew
.
type
===
'
connection
'
)
{
isValid
=
gmeNode
.
isValidTargetOf
(
creatingNew
.
id
,
'
dst
'
);
}
this
.
setState
({
creatingNew
:
{
type
:
creatingNew
.
type
,
id
:
creatingNew
.
id
,
// connection meta-id, ptr-name or set-name.
nodeId
:
creatingNew
.
nodeId
,
target
:
nodeId
,
isValid
,
},
});
}
});
}
...
...
@@ -615,17 +670,17 @@ ${activeSelection.includes(edgeId) ? ' in-active-selection' : ''}`,
/>
{
showNodeMenu
&&
showNodeMenu
.
id
===
activeSelection
[
0
]
?
(
<
Context
Menu
<
SimpleAction
Menu
gmeClient
=
{
gmeClient
}
nodeId
=
{
showNodeMenu
.
id
}
data
=
{
showNodeMenu
.
data
}
eventX
=
{
showNodeMenu
.
position
.
x
}
eventY
=
{
showNodeMenu
.
position
.
y
}
onClose
=
{
()
=>
{
this
.
setState
({
showNodeMenu
:
null
});
}
}
createPointer
=
{
this
.
startNewPointer
}
setActiveNode
=
{
setActiveNode
}
onSetPointerTarget
=
{
this
.
startNewPointer
}
onCreateConnection
=
{
this
.
startNewConnection
}
onSetActiveNode
=
{
setActiveNode
}
/>)
:
null
}
<
FilterSelector
...
...
src/components/SimpleActionMenu/SimpleActionMenu.jsx
0 → 100644
View file @
c281d62c
/* globals document */
/**
* TODO: Consider moving this outside of the GraphEditor and let it be passed as child instead.
* @author pmeijer / https://github.com/pmeijer
*/
import
React
,
{
Component
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
Menu
from
'
@material-ui/core/Menu
'
;
import
MenuItem
from
'
@material-ui/core/MenuItem
'
;
import
ListItemIcon
from
'
@material-ui/core/ListItemIcon
'
;
import
ListItemText
from
'
@material-ui/core/ListItemText
'
;
import
OpenIcon
from
'
@material-ui/icons/ArrowDownward
'
;
import
DeleteIcon
from
'
@material-ui/icons/Clear
'
;
import
PointerIcon
from
'
@material-ui/icons/TrendingFlat
'
;
import
SetIcon
from
'
@material-ui/icons/CallSplit
'
;
import
ConnectionIcon
from
'
@material-ui/icons/SettingsEthernet
'
;
import
NodeIcon
from
'
@material-ui/icons/FiberManualRecord
'
;
function
noop
()
{}
export
default
class
SimpleActionMenu
extends
Component
{
static
propTypes
=
{
gmeClient
:
PropTypes
.
object
.
isRequired
,
nodeId
:
PropTypes
.
string
.
isRequired
,
eventX
:
PropTypes
.
number
.
isRequired
,
eventY
:
PropTypes
.
number
.
isRequired
,
onClose
:
PropTypes
.
func
.
isRequired
,
allowDeletion
:
PropTypes
.
bool
,
onSetActiveNode
:
PropTypes
.
func
,
onSetPointerTarget
:
PropTypes
.
func
,
onAddSetMember
:
PropTypes
.
func
,
onCreateConnection
:
PropTypes
.
func
,
onCreateChild
:
PropTypes
.
func
,
};
static
defaultProps
=
{
allowDeletion
:
true
,
onSetActiveNode
:
noop
,
onSetPointerTarget
:
noop
,
onAddSetMember
:
noop
,
onCreateConnection
:
noop
,
onCreateChild
:
noop
,
}
setActiveNode
=
()
=>
{
const
{
nodeId
,
onSetActiveNode
}
=
this
.
props
;
onSetActiveNode
(
nodeId
);
}
deleteNode
=
()
=>
{
const
{
gmeClient
,
nodeId
,
onClose
}
=
this
.
props
;
gmeClient
.
deleteNode
(
nodeId
);
onClose
();
}
getSetPointerFunc
=
(
ptrName
)
=>
{
const
{
onSetPointerTarget
,
nodeId
}
=
this
.
props
;
return
()
=>
{
onSetPointerTarget
(
nodeId
,
ptrName
);
};
}
getAddMemberFunc
=
(
setName
)
=>
{
const
{
onAddSetMember
,
nodeId
}
=
this
.
props
;
return
()
=>
{
onAddSetMember
(
nodeId
,
setName
);
};
}
getCreateConnectionFunc
=
(
connectionTypeId
)
=>
{
const
{
onCreateConnection
,
nodeId
}
=
this
.
props
;
return
()
=>
{
onCreateConnection
(
nodeId
,
connectionTypeId
);
};
}
render
()
{
const
{
gmeClient
,
nodeId
,
eventX
,
eventY
,
allowDeletion
,
onSetActiveNode
,
onSetPointerTarget
,
onAddSetMember
,
onCreateConnection
,
onCreateChild
,
onClose
,
}
=
this
.
props
;
const
menuItems
=
[];
const
nodeObj
=
gmeClient
.
getNode
(
nodeId
);
const
metaNodeObj
=
gmeClient
.
getNode
(
nodeObj
.
getMetaTypeId
());
const
isReadOnly
=
nodeObj
.
isReadOnly
();
if
(
onSetActiveNode
!==
noop
)
{
menuItems
.
push
((
<
MenuItem
key
=
"open-sub-system"
onClick
=
{
this
.
setActiveNode
}
>
<
ListItemIcon
>
<
OpenIcon
/>
</
ListItemIcon
>
<
ListItemText
>
{
`Open
${
nodeObj
.
getAttribute
(
'
name
'
)}
`
}
</
ListItemText
>
</
MenuItem
>));
}
if
(
onSetPointerTarget
!==
noop
&&
isReadOnly
===
false
)
{
const
pointerNames
=
metaNodeObj
.
getValidPointerNames
();
pointerNames
.
forEach
((
name
)
=>
{
menuItems
.
push
((
<
MenuItem
key
=
{
`create-pointer-
${
name
}
`
}
onClick
=
{
this
.
getSetPointerFunc
(
name
)
}
>
<
ListItemIcon
>
<
PointerIcon
/>
</
ListItemIcon
>
<
ListItemText
>
{
`Create pointer
${
name
}
from here ...`
}
</
ListItemText
>
</
MenuItem
>));
});
}
if
(
onAddSetMember
!==
noop
&&
isReadOnly
===
false
)
{
const
setNames
=
metaNodeObj
.
getValidSetNames
();
setNames
.
forEach
((
name
)
=>
{
menuItems
.
push
((
<
MenuItem
key
=
{
`add-set-member-
${
name
}
`
}
onClick
=
{
this
.
getAddMemberFunc
(
name
)
}
>
<
ListItemIcon
>
<
SetIcon
/>
</
ListItemIcon
>
<
ListItemText
>
{
`Add new member to set
${
name
}
...`
}
</
ListItemText
>
</
MenuItem
>));
});
}
if
(
onCreateConnection
!==
noop
&&
isReadOnly
===
false
)
{
gmeClient
.
getAllMetaNodes
()
.
filter
(
metaNode
=>
metaNode
.
isConnection
()
&&
nodeObj
.
isValidTargetOf
(
metaNode
.
getId
(),
'
src
'
))
.
map
(
metaNode
=>
({
name
:
metaNode
.
getFullyQualifiedName
(),
id
:
metaNode
.
getId
()}))
.
forEach
((
info
)
=>
{
menuItems
.
push
((
<
MenuItem
key
=
{
`create-new-conn-
${
info
}
`
}
onClick
=
{
this
.
getCreateConnectionFunc
(
info
.
id
)
}
>
<
ListItemIcon
>
<
ConnectionIcon
/>
</
ListItemIcon
>
<
ListItemText
>
{
`Create connection
${
info
.
name
}
from here...`
}
</
ListItemText
>
</
MenuItem
>));
});
}
return
(
<
Menu
id
=
"simple-menu"
anchorEl
=
{
document
.
body
}
style
=
{
{
position
:
'
absolute
'
,
top
:
eventY
-
(
document
.
body
.
clientHeight
/
2
),
left
:
eventX
,
}
}
open
onClose
=
{
onClose
}
>
{
menuItems
}
</
Menu
>
);
}
}
src/components/SimpleActionMenu/index.jsx
0 → 100644
View file @
c281d62c
export
{
default
}
from
'
./SimpleActionMenu
'
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment