@@ -158,6 +185,32 @@ const FILTERLOGIC = require('./filter-logic'); // handles filtering functions
request information contained on this website in an accessible
format.
+ {layoutFiltersOpen
+ // OPEN
+ ?
+
+
+
+
+
+ // CLOSED
+ :
+
+
+ }
); // end return
diff --git a/build/app/view/netcreate/components/EdgeTable.jsx b/build/app/view/netcreate/components/EdgeTable.jsx
index 96889d3a..d3ee3306 100644
--- a/build/app/view/netcreate/components/EdgeTable.jsx
+++ b/build/app/view/netcreate/components/EdgeTable.jsx
@@ -4,6 +4,9 @@
EdgeTable is used to to display a table of edges for review.
+ It displays D3DATA.
+ But also read FILTEREDD3DATA to show highlight/filtered state
+
## TO USE
@@ -34,7 +37,7 @@ const isLocalHost = (SETTINGS.EJSProp('client').ip === '127.0.0.1') || (locatio
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const React = require('react');
const ReactStrap = require('reactstrap');
-const { Button, Table } = ReactStrap;
+const { Button } = ReactStrap;
const MarkdownNote = require('./MarkdownNote');
const UNISYS = require('unisys/client');
@@ -51,11 +54,14 @@ class EdgeTable extends UNISYS.Component {
this.state = {
edgePrompts: this.AppState('TEMPLATE').edgePrompts,
edges: [],
+ filteredEdges: [],
isExpanded: true,
sortkey: 'Relationship'
};
+ this.updateEdgeFilterState = this.updateEdgeFilterState.bind(this);
this.handleDataUpdate = this.handleDataUpdate.bind(this);
+ this.handleFilterDataUpdate = this.handleFilterDataUpdate.bind(this);
this.OnTemplateUpdate = this.OnTemplateUpdate.bind(this);
this.onButtonClick = this.onButtonClick.bind(this);
this.onToggleExpanded = this.onToggleExpanded.bind(this);
@@ -65,7 +71,7 @@ class EdgeTable extends UNISYS.Component {
this.setSortKey = this.setSortKey.bind(this);
this.sortSymbol = this.sortSymbol.bind(this);
- this.sortDirection = -1;
+ this.sortDirection = 1;
/// Initialize UNISYS DATA LINK for REACT
@@ -77,25 +83,82 @@ class EdgeTable extends UNISYS.Component {
// Handle Template updates
this.OnAppStateChange('TEMPLATE', this.OnTemplateUpdate);
- } // constructor
+ // Track Filtered Data Updates too
+ this.OnAppStateChange('FILTEREDD3DATA', this.handleFilterDataUpdate);
+ } // constructor
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/*/ Handle updated SELECTION
/*/
- handleDataUpdate(data) {
- // 2020-09-09 Removing this check and relying on other NodeTable optimizations. BL
- // if(data.bMarkedNode)
- // {
- // data.bMarkedNode = false;
- // }
- // else
- // {
+/*/ componentDidMount () {
+ if (DBG) console.log('EdgeTable.componentDidMount!');
+ // Explicitly retrieve data because we may not have gotten a D3DATA
+ // update while we were hidden.
+ // filtered data needs to be set before D3Data
+ const FILTEREDD3DATA = UDATA.AppState('FILTEREDD3DATA');
+ this.setState({ filteredEdges: FILTEREDD3DATA.edges },
+ () => {
+ let D3DATA = this.AppState('D3DATA');
+ this.handleDataUpdate(D3DATA);
+ }
+ )
+}
+
+ componentWillUnmount() {
+ this.AppStateChangeOff('D3DATA', this.handleDataUpdate);
+ this.AppStateChangeOff('FILTEREDD3DATA', this.handleFilterDataUpdate);
+ this.AppStateChangeOff('TEMPLATE', this.OnTemplateUpdate);
+ }
+ displayUpdated(nodeEdge) {
+ var d = new Date(nodeEdge.meta.revision > 0 ? nodeEdge.meta.updated : nodeEdge.meta.created);
+
+ var year = String(d.getFullYear());
+ var date = (d.getMonth()+1)+"/"+d.getDate()+"/"+ year.substr(2,4);
+ var time = d.toTimeString().substr(0,5);
+ var dateTime = date+' at '+time;
+ var titleString = "v" + nodeEdge.meta.revision;
+ if (nodeEdge._nlog) titleString += " by " + nodeEdge._nlog[nodeEdge._nlog.length-1];
+ var tag = {dateTime} ;
+
+ return tag;
+ }
+
+
+ /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /// Set edge filtered status based on current filteredNodes
+ updateEdgeFilterState(edges, filteredEdges) {
+ // add highlight/filter status
+ if (filteredEdges.length > 0) {
+ edges = edges.map(edge => {
+ const filteredEdge = filteredEdges.find(n => n.id === edge.id);
+ edge.isFiltered = !filteredEdge;
+ return edge;
+ });
+ }
+ this.setState({edges});
+ }
+
+ /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ /*/ Handle updated SELECTION
+ /*/
+ handleDataUpdate(data) {
if (data && data.edges) {
const edges = this.sortTable(this.state.sortkey, data.edges);
- this.setState({edges});
+ const { filteredEdges } = this.state;
+ this.updateEdgeFilterState(edges, filteredEdges);
+ }
+ }
+
+ /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ handleFilterDataUpdate(data) {
+ if (data.edges) {
+ const filteredEdges = data.edges;
+ this.setState({ filteredEdges }, () => {
+ const edges = this.sortTable(this.state.sortkey, this.state.edges);
+ this.updateEdgeFilterState(edges, filteredEdges);
+ });
}
}
@@ -113,8 +176,8 @@ class EdgeTable extends UNISYS.Component {
return edges.sort( (a,b) => {
let akey = a.id,
bkey = b.id;
- if (akeybkey) return 1*this.sortDirection;
+ if (akeybkey) return 1*Number(this.sortDirection);
return 0;
});
}
@@ -153,14 +216,14 @@ class EdgeTable extends UNISYS.Component {
return edges.sort( (a,b) => {
let akey = a.attributes[key],
bkey = b.attributes[key];
- if (akeybkey) return 1*this.sortDirection;
+ if (akeybkey) return 1*Number(this.sortDirection);
if (akey===bkey) {
// Secondary sort on Source label
let source_a = a.source.label;
let source_b = b.source.label;
- if (source_asource_b) return 1*this.sortDirection;
+ if (source_asource_b) return 1*Number(this.sortDirection);
}
return 0;
});
@@ -169,19 +232,17 @@ class EdgeTable extends UNISYS.Component {
}
/// ---
- sortByUpdated(edges)
- {
+ sortByUpdated(edges) {
if (edges) {
return edges.sort( (a,b) => {
let akey = (a.meta.revision > 0 ? a.meta.updated : a.meta.created),
bkey = (b.meta.revision > 0 ? b.meta.updated : b.meta.created);
- if (akeybkey) return 1*this.sortDirection;
+ if (akeybkey) return 1*Number(this.sortDirection);
return 0;
});
}
return undefined;
-
}
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*/ If no `sortkey` is passed, the sort will use the existing state.sortkey
@@ -196,9 +257,6 @@ class EdgeTable extends UNISYS.Component {
case 'target':
return this.sortByTargetLabel(edges);
break;
- case 'Relationship':
- return this.sortByAttribute(edges, 'Relationship');
- break;
case 'Info':
return this.sortByAttribute(edges, 'Info');
break;
@@ -221,12 +279,9 @@ class EdgeTable extends UNISYS.Component {
}
}
- sortSymbol(key)
- {
- if(key != this.state.sortkey) // this is not the current sort, so don't show anything
- return "";
- else
- return this.sortDirection==-1?"▼":"▲"; // default to "decreasing" and flip if clicked again
+ sortSymbol(key) {
+ if (key !== this.state.sortkey) return ""; // this is not the current sort, so don't show anything
+ else return this.sortDirection===1?"▼":"▲"; // default to "decreasing" and flip if clicked again
}
/// UI EVENT HANDLERS /////////////////////////////////////////////////////////
@@ -257,11 +312,8 @@ class EdgeTable extends UNISYS.Component {
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*/
/*/ setSortKey (key) {
-
- if(key == this.state.sortkey)
- this.sortDirection = (-1 * this.sortDirection);// if this was already the key, flip the direction
- else
- this.sortDirection = 1;
+ if (key === this.state.sortkey) this.sortDirection = (-1 * this.sortDirection);// if this was already the key, flip the direction
+ else this.sortDirection = 1;
const edges = this.sortTable(key, this.state.edges);
this.setState({
@@ -326,39 +378,54 @@ class EdgeTable extends UNISYS.Component {
/*/ render () {
let { edgePrompts } = this.state;
- if(edgePrompts.category == undefined) // for backwards compatability
- {
+ if (edgePrompts.category === undefined) { // for backwards compatability
edgePrompts.category = {};
edgePrompts.category.label = "";
edgePrompts.category.hidden = true;
}
-
- let { tableHeight } = this.props;
- let styles = `
- thead, tbody { }
- thead { position: relative; }
- tbody { overflow: auto; }
-`
+ const { tableHeight } = this.props;
+ const styles = `thead, tbody { font-size: 0.8em }
+ .table {
+ display: table; /* override bootstrap for fixed header */
+ border-spacing: 0;
+ }
+ .table th {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 0;
+ background-color: #eafcff;
+ border-top: none;
+ }
+ xtbody { overflow: auto; }
+ .btn-sm { font-size: 0.6rem; padding: 0.1rem 0.2rem }
+ `
return (
-
+
-
+
-
Size
-
_Edit_
+
_Edit_
Src ID
this.setSortKey("source")}
@@ -376,13 +443,13 @@ class EdgeTable extends UNISYS.Component {
This is a collection of nodes and the edges between them.
-
-
Graph
-
a graphic representation of a network and its components. Similar terms include: sociogram, visualization
-
-
Node
-
The thing or entity (shown as circles) that is connected through relationships. This could be individual people, groups of people, institutions (like churches, organizations, schools). One way of thinking about this is that nodes are nouns and edges are verbs - nodes are things that are connected through edges. Similar terms include: actor, vertex
-
-
-
Ego
-
This refers to the node you are focused on at the moment and the connections that they have.
-
-
-
-
Edge
-
The relationships between nodes you are considering (shown as lines). Relationships can take on many forms: nodes could be connected through somewhat intangible relationships, such as friendship or not liking one another. Edges can be based on interactions, such as talking to one another or being in conflict. They could also be defined by sharing resources, such as money or information. Similar terms include: line, tie, arc
-
-
-
Edge weight
-
Edges can have a value attached to them. So, for instance, an node could sent $10,000 to another actor. Or, they could share three interactions of the same type with one another. This value is referred to as a weight. Similar terms include: value
-
-
Directed or undirected edges
-
Edges can either be directed or undirected. If a relationship is directed, it is being sent from (originating from) one node to another node. Node A may say they are friends with Node B, but Node B does not say Node A does this. Or Node A gives Node B something, such as resources, information, or an illness. However, in some cases, edges are defined as undirected. Two people who share a meal together or are married are both engaged share an undirected edge. Note: in some academic literatures, the term "edge" is reserved for an undirected relationship, while the term "arc" is used to refer to directed ties.
-
-
-
Attributes
-
Characteristics of the nodes or edges. A node could be designated by gender, for instance or the amount of wealth they possess. They could also be characteristics you find from the network itself - such as how many ties an node has (degree centrality).
-
-
Centrality
-
This is a way of ranking the importance of individuals within a network. There are many different ways to measure importance, such as degree centrality, betweenness centrality, and eigenvector centrality.
-
-
-
Degree Centrality
-
Degree centrality is a measure of how many connections a node has. An node with many ties that are being sent to them has a high in-degree centrality. In a friendship network, this can be easily recognized as popularity. Nodes sending many outgoing ties (high out-degree centrality) may be thought of as expansive in their relationship.
-
-
Betweenness Centrality
-
Nodes with high betweenness centrality serve as connectors between other individuals who wouldn't otherwise be directly connected. They may not be connected to a large number of people (that would be high degree centrality), but they are unique in their connections. If an actor with high betweenness centrality was removed from a network, the network would be more fragmented and less connected. Often researchers are interested in finding actors with high betweenness centrality because they can control whatever flows in the networks. For instance, military analysts often look for nodes with high betweenness in a terrorist network.
-
-
Eigenvector Centrality
-
Eigenvector centrality ranks actors based on their connection to other highly central nodes. So, a nodes importance as measured by eigenvector centrality are dependent on the other nodes with whom they share connections. Google's PageRank algorithm was a famous application of a version of this type of centrality, and allowed them to return highly relevant results in search for users.
-
-
-
Communities
-
A community in a network is a way of thinking about grouping, often by finding densely connected sets of nodes. A community within a network that is tightly connected to one another but not to an outside group might be seen as a faction, such as rival political groups. In this case, nodes with high betweenness centrality in a network with multiple factions might be some of the only points of contact between rival groups - a potentially powerful but also difficult position to be in.
This is a collection of nodes and the edges between them.
+
+
Graph
+
a graphic representation of a network and its components. Similar terms include: sociogram, visualization
+
+
Node
+
The thing or entity (shown as circles) that is connected through relationships. This could be individual people, groups of people, institutions (like churches, organizations, schools). One way of thinking about this is that nodes are nouns and edges are verbs - nodes are things that are connected through edges. Similar terms include: actor, vertex
+
+
+
Ego
+
This refers to the node you are focused on at the moment and the connections that they have.
+
+
+
+
Edge
+
The relationships between nodes you are considering (shown as lines). Relationships can take on many forms: nodes could be connected through somewhat intangible relationships, such as friendship or not liking one another. Edges can be based on interactions, such as talking to one another or being in conflict. They could also be defined by sharing resources, such as money or information. Similar terms include: line, tie, arc
+
+
+
Edge weight
+
Edges can have a value attached to them. So, for instance, an node could sent $10,000 to another actor. Or, they could share three interactions of the same type with one another. This value is referred to as a weight. Similar terms include: value
+
+
Directed or undirected edges
+
Edges can either be directed or undirected. If a relationship is directed, it is being sent from (originating from) one node to another node. Node A may say they are friends with Node B, but Node B does not say Node A does this. Or Node A gives Node B something, such as resources, information, or an illness. However, in some cases, edges are defined as undirected. Two people who share a meal together or are married are both engaged share an undirected edge. Note: in some academic literatures, the term "edge" is reserved for an undirected relationship, while the term "arc" is used to refer to directed ties.
+
+
+
Attributes
+
Characteristics of the nodes or edges. A node could be designated by gender, for instance or the amount of wealth they possess. They could also be characteristics you find from the network itself - such as how many ties an node has (degree centrality).
+
+
Centrality
+
This is a way of ranking the importance of individuals within a network. There are many different ways to measure importance, such as degree centrality, betweenness centrality, and eigenvector centrality.
+
+
+
Degree Centrality
+
Degree centrality is a measure of how many connections a node has. An node with many ties that are being sent to them has a high in-degree centrality. In a friendship network, this can be easily recognized as popularity. Nodes sending many outgoing ties (high out-degree centrality) may be thought of as expansive in their relationship.
+
+
Betweenness Centrality
+
Nodes with high betweenness centrality serve as connectors between other individuals who wouldn't otherwise be directly connected. They may not be connected to a large number of people (that would be high degree centrality), but they are unique in their connections. If an actor with high betweenness centrality was removed from a network, the network would be more fragmented and less connected. Often researchers are interested in finding actors with high betweenness centrality because they can control whatever flows in the networks. For instance, military analysts often look for nodes with high betweenness in a terrorist network.
+
+
Eigenvector Centrality
+
Eigenvector centrality ranks actors based on their connection to other highly central nodes. So, a nodes importance as measured by eigenvector centrality are dependent on the other nodes with whom they share connections. Google's PageRank algorithm was a famous application of a version of this type of centrality, and allowed them to return highly relevant results in search for users.
+
+
+
Communities
+
A community in a network is a way of thinking about grouping, often by finding densely connected sets of nodes. A community within a network that is tightly connected to one another but not to an outside group might be seen as a faction, such as rival political groups. In this case, nodes with high betweenness centrality in a network with multiple factions might be some of the only points of contact between rival groups - a potentially powerful but also difficult position to be in.
+
+
+ );
+ }
} // class Vocabulary
diff --git a/build/app/view/netcreate/components/d3-simplenetgraph.js b/build/app/view/netcreate/components/d3-simplenetgraph.js
index 3d387f23..c4efdb18 100644
--- a/build/app/view/netcreate/components/d3-simplenetgraph.js
+++ b/build/app/view/netcreate/components/d3-simplenetgraph.js
@@ -2,6 +2,8 @@
D3 Simple NetGraph
+ This uses D3 Version 4.0.
+
This is designed to work with the NetGraph React component.
NetGraph calls SetData whenever it receives an updated data object.
@@ -165,10 +167,20 @@ class D3NetGraph {
this._Dragged = this._Dragged.bind(this);
this._Dragended = this._Dragended.bind(this);
+ // V1.4 CHANGE
+ // Ignore D3DATA Updates!!! Only listen to FILTEREDD3DATA Updates
+ //
// watch for updates to the D3DATA data object
- UDATA.OnAppStateChange('D3DATA',(data)=>{
+ // UDATA.OnAppStateChange('D3DATA',(data)=>{
+ // // expect { nodes, edges } for this namespace
+ // if (DBG) console.error(PR,'got state D3DATA',data);
+ // this._SetData(data);
+ // });
+
+ // Special handler for the remove filter
+ UDATA.OnAppStateChange('FILTEREDD3DATA',(data)=>{
// expect { nodes, edges } for this namespace
- if (DBG) console.log(PR,'got state D3DATA',data);
+ if (DBG) console.log(PR,'got state FILTEREDD3DATA',data);
this._SetData(data);
});
@@ -182,6 +194,7 @@ class D3NetGraph {
UDATA.HandleMessage('ZOOM_RESET', (data) => {
if (DBG) console.log(PR, 'ZOOM_RESET got state D3DATA', data);
+ // NOTE: The transition/duration call means _HandleZoom will be called multiple times
this.d3svg.transition()
.duration(200)
.call(this.zoom.scaleTo, 1);
@@ -197,6 +210,13 @@ class D3NetGraph {
this._Transition(0.8);
});
+ // Pan to 0,0 and zoom scale to 1
+ // (Currently not used)
+ UDATA.HandleMessage('ZOOM_PAN_RESET', (data) => {
+ if (DBG) console.log(PR, 'ZOOM_PAN_RESET got state D3DATA', data);
+ const transform = d3.zoomIdentity.translate(0, 0).scale(1);
+ this.d3svg.call(this.zoom.transform, transform);
+ });
UDATA.HandleMessage('GROUP_PROPS', (data) => {
console.log('GROUP_PROPS got ... ');
@@ -331,7 +351,7 @@ class D3NetGraph {
return COLORMAP[d.attributes["Node_Type"]];
})
.style("opacity", d => {
- return d.isFilteredOut ? d.filteredTransparency : 1.0
+ return d.filteredTransparency
});
// enter node: also append 'text' element
@@ -343,7 +363,7 @@ class D3NetGraph {
.attr("dy", "0.35em") // ".15em")
.text((d) => { return d.label })
.style("opacity", d => {
- return d.isFilteredOut ? d.filteredTransparency : 1.0
+ return d.filteredTransparency
});
// enter node: also append a 'title' tag
@@ -403,26 +423,28 @@ class D3NetGraph {
if (d.selected || d.strokeColor) return '5px';
return undefined // don't set stroke width
})
-// this "r" is necessary to resize after a link is added
.attr("fill", (d) => {
// REVIEW: Using label match. Should we use id instead?
return COLORMAP[d.attributes["Node_Type"]];
})
.attr("r", (d) => {
- let radius = this.data.edges.reduce((acc,ed)=>{
- return (ed.source.id===d.id || ed.target.id===d.id) ? acc+1 : acc
- },1);
-
- d.weight = radius
- d.size = radius // save the calculated size
- d.degrees = radius - 1
- return this.defaultSize + (this.defaultSize * d.weight / 2)
+ // this "r" is necessary to resize after a link is added
+ let radius = this.data.edges.reduce((acc,ed)=>{
+ return (ed.source.id===d.id || ed.target.id===d.id) ? acc+1 : acc
+ },1);
+
+ d.weight = radius
+ d.size = radius // save the calculated size
+ // radius is calculated by counting the number of edges attached
+ // (+ 1 for a minimum radius), so we hack degrees by using radius-1
+ d.degrees = radius - 1
+ return this.defaultSize + (this.defaultSize * d.weight / 2)
})
.transition()
.duration(500)
.style("opacity", d => {
// console.log(d);
- return d.isFilteredOut ? d.filteredTransparency : 1.0
+ return d.filteredTransparency
});
// UPDATE text in each node for all nodes
@@ -443,7 +465,7 @@ class D3NetGraph {
.transition()
.duration(500)
.style("opacity", d => {
- return d.isFilteredOut ? d.filteredTransparency : 1.0
+ return d.filteredTransparency
});
nodeElements.merge(nodeElements)
@@ -471,7 +493,7 @@ class D3NetGraph {
// this.edgeClickFn( d )
// })
.style("opacity", d => {
- return d.isFilteredOut ? d.filteredTransparency : 1.0
+ return d.filteredTransparency
});
// .merge() updates the visuals whenever the data is updated.
@@ -482,7 +504,7 @@ class D3NetGraph {
.transition()
.duration(500)
.style("opacity", d => {
- return d.isFilteredOut ? d.filteredTransparency : 1.0
+ return d.filteredTransparency
});
linkElements.exit().remove()
diff --git a/build/app/view/netcreate/components/filter/FilterEnums.js b/build/app/view/netcreate/components/filter/FilterEnums.js
index 5dff9f90..2c4e0b39 100644
--- a/build/app/view/netcreate/components/filter/FilterEnums.js
+++ b/build/app/view/netcreate/components/filter/FilterEnums.js
@@ -1,5 +1,10 @@
const FILTER = {};
+// Determines whether filter action is to highlight/fade or remove (filter) nodes and edges
+FILTER.ACTION = {};
+FILTER.ACTION.HIGHLIGHT = 'highlight';
+FILTER.ACTION.FILTER = 'filter';
+
// Types of filters definable in template files.
FILTER.TYPES = {};
FILTER.TYPES.STRING = 'string';
diff --git a/build/app/view/netcreate/components/filter/FilterGroup.jsx b/build/app/view/netcreate/components/filter/FilterGroup.jsx
index 488ffbee..83e685f1 100644
--- a/build/app/view/netcreate/components/filter/FilterGroup.jsx
+++ b/build/app/view/netcreate/components/filter/FilterGroup.jsx
@@ -8,7 +8,7 @@ const ReactStrap = require('reactstrap');
const { Input, Label } = ReactStrap;
export default function FilterGroup({
- group, label, filters, transparency
+ group, label, filters, filterAction, transparency
}) {
return (
@@ -17,13 +17,13 @@ export default function FilterGroup({
switch (filter.type) {
case FILTER.TYPES.STRING:
case FILTER.TYPES.NODE:
- return
+ return
break;
case FILTER.TYPES.NUMBER:
- return
+ return
break;
case FILTER.TYPES.SELECT:
- return
+ return
break;
default:
console.error(`FilterGroup: Filter Type not found ${filter.type} for filter`, filter);
@@ -31,8 +31,8 @@ export default function FilterGroup({
}
return '';
})}
-
+