diff --git a/build/app/assets/templates/alexander.json b/build/app/assets/templates/alexander.json
index af9766a0f..c74ae36dd 100644
--- a/build/app/assets/templates/alexander.json
+++ b/build/app/assets/templates/alexander.json
@@ -19,6 +19,7 @@
"label": {
"label": "Label",
"help": "A short title for the node",
+ "duplicateWarning": "You’re entering a duplicate node. Do you want to View the Existing node, or Continue creating?",
"_cmt4": "/// `Label` is always required and cannot be hidden"
},
"type": {
@@ -59,6 +60,11 @@
}
]
},
+ "degrees": {
+ "label": "Degrees",
+ "help": "Number of edges.",
+ "hidden": false
+ },
"notes": {
"label": "Significance",
"help": "Add some details.",
@@ -68,7 +74,10 @@
"label": "Geocode or Date",
"help": "Use latitude/longitude or a date mm/dd/yyy",
"hidden": true
- }
+ },
+ "delete": {
+ "hidden": false
+ }
},
diff --git a/build/app/unisys/server-database.js b/build/app/unisys/server-database.js
index 64fcdfe4d..566e42b69 100644
--- a/build/app/unisys/server-database.js
+++ b/build/app/unisys/server-database.js
@@ -139,7 +139,7 @@ let DB = {};
};
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DB.PKT_Update = function ( pkt ) {
- let { node, edge, edgeID } = pkt.Data();
+ let { node, edge, nodeID, replacementNodeID, edgeID } = pkt.Data();
let retval = {};
// PROCESS NODE INSERT/UPDATE
@@ -192,6 +192,43 @@ let DB = {};
return retval;
} // if edge
+ // DELETE NODES
+ if (nodeID !== undefined) {
+ if (DBG) console.log(PR, `PKT_Update ${pkt.Info()} DELETE nodeID ${nodeID}`);
+
+ // Log first so it's apparent what is triggering the edge changes
+ LOGGER.Write(pkt.Info(), `delete node`, nodeID);
+
+ // handle edges
+ let edgesToProcess = EDGES.where((e) => {
+ return e.source === nodeID || e.target === nodeID;
+ });
+ // `NaN` is not valid JSON, so we use ``
+ if (replacementNodeID !== '') {
+ // re-link edges to replacementNodeID
+ EDGES.findAndUpdate({ source: nodeID }, (e) => {
+ LOGGER.Write(`...`, pkt.Info(), `relinking edge`, e.id, `to`, replacementNodeID);
+ e.source = replacementNodeID;
+ });
+ EDGES.findAndUpdate({ target: nodeID }, (e) => {
+ LOGGER.Write(`...`, pkt.Info(), `relinking edge`, e.id, `to`, replacementNodeID);
+ e.target = replacementNodeID;
+ });
+ } else {
+ // delete edges
+ EDGES.findAndRemove({ source: nodeID }, (e) => {
+ LOGGER.Write(`...`, pkt.Info(), `deleting edge`, e.id, `from`, nodeID);
+ e.source = nodeID;
+ });
+ EDGES.findAndRemove({ target: nodeID }, (e) => {
+ LOGGER.Write(`...`, pkt.Info(), `deleting edge`, e.id, `from`, nodeID);
+ e.target = nodeID;
+ });
+ }
+ NODES.findAndRemove({ id: nodeID });
+ return { op: 'delete', nodeID, replacementNodeID };
+ }
+
// DELETE EDGES
if (edgeID!==undefined) {
if (DBG) console.log(PR,`PKT_Update ${pkt.Info()} DELETE edgeID ${edgeID}`);
diff --git a/build/app/unisys/server.js b/build/app/unisys/server.js
index 33b14886b..ec209b9fd 100644
--- a/build/app/unisys/server.js
+++ b/build/app/unisys/server.js
@@ -71,7 +71,8 @@ var UNISYS = {};
data.src = 'remote';
// fire update messages
if (data.node) UNET.NetSend('SOURCE_UPDATE',data);
- if (data.edge) UNET.NetSend('EDGE_UPDATE',data);
+ if (data.edge) UNET.NetSend('EDGE_UPDATE', data);
+ if (data.nodeID!==undefined) UNET.NetSend('NODE_DELETE', data);
if (data.edgeID!==undefined) UNET.NetSend('EDGE_DELETE',data);
// return SRV_DBUPDATE value (required)
return { OK:true, info:'SRC_DBUPDATE' };
diff --git a/build/app/view/netcreate/NetCreate.jsx b/build/app/view/netcreate/NetCreate.jsx
index 124f2cdab..bac32b373 100644
--- a/build/app/view/netcreate/NetCreate.jsx
+++ b/build/app/view/netcreate/NetCreate.jsx
@@ -47,9 +47,7 @@ const { Route } = require('react-router-dom');
const NetGraph = require('./components/NetGraph');
const Search = require('./components/Search');
const NodeSelector = require('./components/NodeSelector');
-const Help = require('./components/Help');
-const NodeTable = require('./components/NodeTable');
-const EdgeTable = require('./components/EdgeTable');
+const InfoPanel = require('./components/InfoPanel');
const NCLOGIC = require('./nc-logic'); // require to bootstrap data loading
@@ -77,11 +75,17 @@ const NCLOGIC = require('./nc-logic'); // require to bootstrap data loading
});
}
+
+
+
/// REACT LIFECYCLE METHODS ///////////////////////////////////////////////////
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*/ This is the root component, so this fires after all subcomponents have
been fully rendered by render().
/*/ componentDidMount () {
+ // Init dragger
+ let dragger = document.getElementById('dragger');
+ dragger.onmousedown = this.handleMouseDown;
}
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*/ Define the component structure of the web application
@@ -100,9 +104,7 @@ const NCLOGIC = require('./nc-logic'); // require to bootstrap data loading
-
-
-
+
Please contact Professor
Kalani Craig, Institute for Digital Arts & Humanities at
diff --git a/build/app/view/netcreate/components/AutoComplete.css b/build/app/view/netcreate/components/AutoComplete.css
index e7e3b8f46..1261fedad 100644
--- a/build/app/view/netcreate/components/AutoComplete.css
+++ b/build/app/view/netcreate/components/AutoComplete.css
@@ -92,6 +92,11 @@ td {
hyphens: auto;
}
+/* Bootstrap override */
+.nav-item {
+ cursor: pointer;
+}
+
/* SVG styles */
/* REVIEW!!! These are global!!! */
svg {
diff --git a/build/app/view/netcreate/components/AutoComplete.jsx b/build/app/view/netcreate/components/AutoComplete.jsx
index 5e940718f..46b201714 100644
--- a/build/app/view/netcreate/components/AutoComplete.jsx
+++ b/build/app/view/netcreate/components/AutoComplete.jsx
@@ -340,7 +340,7 @@ class AutoComplete extends UNISYS.Component {
https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html
/*/ componentDidMount () {
_IsMounted = true;
- this.setState({ mode: this.props.inactiveMode })
+ this.setState({ mode: this.props.inactiveMode });
}
/*/
/*/ componentWillUnmount () {
@@ -357,34 +357,75 @@ class AutoComplete extends UNISYS.Component {
onChange : this.onInputChange
};
let jsx;
- switch (this.state.mode) {
- case MODE_STATIC:
- jsx = (
{this.props.disabledValue}
);
- break;
- case MODE_DISABLED:
- jsx = ( );
- break;
- case MODE_ACTIVE:
- jsx = (
-
- );
- break;
- default:
- throw Error(`AutoComplete: Unhandled mode '${this.state.mode}'`);
+
+ // Show different widgets depending on mode.
+ // If MODE_ACTIVE is just show the active state,
+ // otherwise, use the current inactive mode in this.props.inactiveMode
+ // to define the inactive state
+ // because this.state.mode may not be up to date if the mode is inactive
+ // due to prop changes not triggering mode updates.
+ // e.g. if the parent container changed props from a disabled to
+ // static state, it does not trigger a mode update in AUTOCOMPLETE.
+ // This is mostly an edge case with EDGE_EDITs which will update props
+ // without a corresponding UNISYS message call to trigger the mode
+ // change.
+ if (this.state.mode === MODE_ACTIVE) {
+ jsx = (
+
+ );
+ } else if (this.props.inactiveMode === MODE_STATIC) {
+ jsx = (
{this.props.disabledValue}
);
+ } else if (this.props.inactiveMode === MODE_DISABLED) {
+ jsx = ();
+ } else {
+ throw Error(`AutoComplete: Unhandled mode '${this.state.mode}'`);
}
+
+ // OLD METHOD
+ // This relied on mode being updated, but a change in props does not
+ // trigger a corresponding change in mode.
+ // switch (this.state.mode) {
+ // case MODE_STATIC:
+ // jsx = (
{this.props.disabledValue}
);
+ // break;
+ // case MODE_DISABLED:
+ // jsx = ( );
+ // break;
+ // case MODE_ACTIVE:
+ // jsx = (
+ //
+ // );
+ // break;
+ // default:
+ // throw Error(`AutoComplete: Unhandled mode '${this.state.mode}'`);
+ // }
+
return jsx;
} // render()
diff --git a/build/app/view/netcreate/components/EdgeEditor.jsx b/build/app/view/netcreate/components/EdgeEditor.jsx
index cd01ca032..f82447982 100644
--- a/build/app/view/netcreate/components/EdgeEditor.jsx
+++ b/build/app/view/netcreate/components/EdgeEditor.jsx
@@ -249,7 +249,7 @@ class EdgeEditor extends UNISYS.Component {
// as a handler, otherwise object context is lost
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- /*/ SESSION is called by SessionSHell when the ID changes
+ /*/ SESSION is called by SessionShell when the ID changes
set system-wide. data: { classId, projId, hashedId, groupId, isValid }
/*/ this.OnAppStateChange('SESSION',this.onStateChange_SESSION);
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -313,12 +313,16 @@ class EdgeEditor extends UNISYS.Component {
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*/ populate formdata from D3DATA
/*/ loadSourceAndTarget () {
- if (DBG) console.log('EdgeEditor.loadSourceAndTarget!')
+ if (DBG) console.log('EdgeEditor.loadSourceAndTarget!')
+
let edgeID = this.props.edgeID || '';
-
+ // Clean Data
+ if (isNaN(edgeID)) { edgeID = parseInt(edgeID); }
+
let D3DATA = this.AppState('D3DATA');
- let edges = D3DATA.edges.filter( edge=>edge.id===edgeID );
+ // parseInt in case of old bad string id
+ let edges = D3DATA.edges.filter( edge=>parseInt(edge.id)===edgeID );
if (!edges) {
throw 'EdgeEditor: Passed edgeID'+edgeID+'not found!';
}
@@ -344,7 +348,7 @@ class EdgeEditor extends UNISYS.Component {
// Define `edge` so it can be loaded later during setState.
edge = {
id: edgeID,
- source: sourceNodes[0].id, // REVIEW: d3data 'source' is id, rename this to 'sourceId'?
+ source: parseInt(sourceNodes[0].id), // REVIEW: d3data 'source' is id, rename this to 'sourceId'?
// though after d3 processes, source does become an object.
target: undefined,
attributes: {
@@ -365,8 +369,8 @@ class EdgeEditor extends UNISYS.Component {
// LOAD EXISTING EDGE
- sourceNodes = D3DATA.nodes.filter( node => node.id===edge.source.id );
- targetNodes = D3DATA.nodes.filter( node => node.id===edge.target.id );
+ sourceNodes = D3DATA.nodes.filter( node => parseInt(node.id)===parseInt(edge.source.id) );
+ targetNodes = D3DATA.nodes.filter( node => parseInt(node.id)===parseInt(edge.target.id) );
// Assume we have a valid target node
this.setState({
@@ -388,7 +392,7 @@ class EdgeEditor extends UNISYS.Component {
if (DBG) console.log('...EdgeEditor.loadSourceAndTarget: Setting formData sourceID to',edge.source,'and sourceNode to',sourceNode,'and targetNode to',targetNode);
this.setState({
formData: {
- id: edge.id || '',
+ id: parseInt(edge.id) || '',
sourceId: edge.source,
targetId: edge.target,
relationship: edge.attributes["Relationship"] || '', // Make sure there's valid data
@@ -430,7 +434,7 @@ class EdgeEditor extends UNISYS.Component {
// SOURCE
if (DBG) console.log('EdgeEditor.handleSelection:',this.props.edgeID,'setting source node to',node);
- // Set sourceNpde state
+ // Set sourceNode state
this.setState({
sourceNode: node
});
@@ -447,7 +451,7 @@ class EdgeEditor extends UNISYS.Component {
sourceIsEditable: false
});
- } else {
+ } else if (this.state.targetIsEditable) {
// TARGET
if (DBG) console.log('EdgeEditor.handleSelection:',this.props.edgeID,'setting target node to',node);
diff --git a/build/app/view/netcreate/components/EdgeTable.jsx b/build/app/view/netcreate/components/EdgeTable.jsx
index 37b3ee2d5..b95cd3220 100644
--- a/build/app/view/netcreate/components/EdgeTable.jsx
+++ b/build/app/view/netcreate/components/EdgeTable.jsx
@@ -14,6 +14,13 @@
Set `DBG` to true to show the `ID` column.
+ ## 2018-12-07 Update
+
+ Since we're not using tab navigation:
+ 1. The table isExpanded is now true by default.
+ 2. The "Show/Hide Table" button is hidden.
+
+ Reset these to restore previous behavior.
\*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/
@@ -38,7 +45,7 @@ class EdgeTable extends UNISYS.Component {
this.state = {
edgePrompts: this.AppState('TEMPLATE').edgePrompts,
edges: [],
- isExpanded: false,
+ isExpanded: true,
sortkey: 'Citations'
};
@@ -259,13 +266,27 @@ class EdgeTable extends UNISYS.Component {
/*/
/*/ render () {
let { edgePrompts } = this.state;
+ let { tableHeight } = this.props;
+ let styles = `thead, tbody { display: block; }
+ thead { position: relative; }
+ tbody { overflow: auto; }
+ .edgetable td:nth-child(1), .edgetable th:nth-child(1) {width: 2em; min-width: 2em;}
+ .edgetable td:nth-child(2), .edgetable th:nth-child(2) {width: 2em; min-width: 2em;}
+ .edgetable td:nth-child(3), .edgetable th:nth-child(3) {width: 4em; min-width: 4em;}
+ .edgetable td:nth-child(4), .edgetable th:nth-child(4) {width: 6em; min-width: 6em;}
+ .edgetable td:nth-child(5), .edgetable th:nth-child(5) {width: 14em; min-width: 14em;}
+ .edgetable td:nth-child(6), .edgetable th:nth-child(6) {width: 6em; min-width: 6em;}
+ .edgetable td:nth-child(7), .edgetable th:nth-child(7) {width: 6em; min-width: 6em;}
+ .edgetable td:nth-child(8), .edgetable th:nth-child(8) {min-width: 6em; }`
return (
-