2022-08-30 15:36:33 +00:00
|
|
|
/*
|
|
|
|
Copyright 2021-present The maxGraph project Contributors
|
|
|
|
Copyright (c) 2006-2020, JGraph Ltd
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-09-07 09:07:27 +00:00
|
|
|
import {
|
|
|
|
Graph,
|
|
|
|
Rectangle,
|
2022-01-08 01:49:35 +00:00
|
|
|
DomHelpers,
|
|
|
|
KeyHandler,
|
2021-09-07 09:07:27 +00:00
|
|
|
InternalEvent,
|
2022-01-08 01:49:35 +00:00
|
|
|
xmlUtils,
|
|
|
|
Codec,
|
|
|
|
constants,
|
2021-09-07 09:07:27 +00:00
|
|
|
utils,
|
|
|
|
EdgeStyle,
|
2022-01-08 01:49:35 +00:00
|
|
|
domUtils,
|
|
|
|
MaxForm,
|
2021-09-07 09:07:27 +00:00
|
|
|
CellAttributeChange,
|
|
|
|
} from '@maxgraph/core';
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
import { globalTypes } from '../.storybook/preview';
|
|
|
|
|
|
|
|
export default {
|
|
|
|
title: 'Xml_Json/UserObject',
|
|
|
|
argTypes: {
|
2021-08-30 14:20:26 +00:00
|
|
|
...globalTypes,
|
|
|
|
},
|
2021-04-25 03:39:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const Template = ({ label, ...args }) => {
|
|
|
|
const div = document.createElement('div');
|
|
|
|
|
2022-05-08 09:05:22 +00:00
|
|
|
const table = document.createElement('table');
|
|
|
|
table.style.position = 'relative';
|
|
|
|
|
|
|
|
table.innerHTML = `
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
<div
|
|
|
|
id="graphContainer"
|
|
|
|
style="border:solid 1px black;overflow:hidden;width:321px;height:241px;cursor:default"
|
|
|
|
></div>
|
|
|
|
</td>
|
|
|
|
<td valign="top">
|
|
|
|
<div
|
|
|
|
id="properties"
|
|
|
|
style="border:solid 1px black;padding:10px"
|
|
|
|
></div>
|
|
|
|
</tr>
|
|
|
|
`;
|
|
|
|
div.appendChild(table);
|
|
|
|
|
|
|
|
const container = document.getElementById('graphContainer');
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Note that these XML nodes will be enclosing the
|
2021-08-30 14:20:26 +00:00
|
|
|
// Cell nodes for the model cells in the output
|
2022-01-08 01:49:35 +00:00
|
|
|
const doc = xmlUtils.createXmlDocument();
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
const person1 = doc.createElement('Person');
|
|
|
|
person1.setAttribute('firstName', 'Daffy');
|
|
|
|
person1.setAttribute('lastName', 'Duck');
|
|
|
|
|
|
|
|
const person2 = doc.createElement('Person');
|
|
|
|
person2.setAttribute('firstName', 'Bugs');
|
|
|
|
person2.setAttribute('lastName', 'Bunny');
|
|
|
|
|
|
|
|
const relation = doc.createElement('Knows');
|
|
|
|
relation.setAttribute('since', '1985');
|
|
|
|
|
|
|
|
// Creates the graph inside the given container
|
2021-08-30 14:20:26 +00:00
|
|
|
const graph = new Graph(container);
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Optional disabling of sizing
|
|
|
|
graph.setCellsResizable(false);
|
|
|
|
|
|
|
|
// Configures the graph contains to resize and
|
|
|
|
// add a border at the bottom, right
|
|
|
|
graph.setResizeContainer(true);
|
2021-08-30 14:20:26 +00:00
|
|
|
graph.minimumContainerSize = new Rectangle(0, 0, 500, 380);
|
2021-04-25 03:39:40 +00:00
|
|
|
graph.setBorder(60);
|
|
|
|
|
|
|
|
// Stops editing on enter key, handles escape
|
2022-01-08 01:49:35 +00:00
|
|
|
new KeyHandler(graph);
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Overrides method to disallow edge label editing
|
2021-08-30 14:20:26 +00:00
|
|
|
graph.isCellEditable = function (cell) {
|
2021-04-25 03:39:40 +00:00
|
|
|
return !cell.isEdge();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Overrides method to provide a cell label in the display
|
2021-08-30 14:20:26 +00:00
|
|
|
graph.convertValueToString = function (cell) {
|
2022-01-08 01:49:35 +00:00
|
|
|
if (domUtils.isNode(cell.value)) {
|
2021-04-25 03:39:40 +00:00
|
|
|
if (cell.value.nodeName.toLowerCase() == 'person') {
|
|
|
|
const firstName = cell.getAttribute('firstName', '');
|
|
|
|
const lastName = cell.getAttribute('lastName', '');
|
|
|
|
|
|
|
|
if (lastName != null && lastName.length > 0) {
|
|
|
|
return `${lastName}, ${firstName}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return firstName;
|
|
|
|
}
|
|
|
|
if (cell.value.nodeName.toLowerCase() == 'knows') {
|
2021-08-30 14:20:26 +00:00
|
|
|
return `${cell.value.nodeName} (Since ${cell.getAttribute('since', '')})`;
|
2021-04-25 03:39:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
};
|
|
|
|
|
|
|
|
// Overrides method to store a cell label in the model
|
|
|
|
const { cellLabelChanged } = graph;
|
2021-08-30 14:20:26 +00:00
|
|
|
graph.cellLabelChanged = function (cell, newValue, autoSize) {
|
2022-01-08 01:49:35 +00:00
|
|
|
if (domUtils.isNode(cell.value) && cell.value.nodeName.toLowerCase() == 'person') {
|
2021-04-25 03:39:40 +00:00
|
|
|
const pos = newValue.indexOf(' ');
|
|
|
|
|
|
|
|
const firstName = pos > 0 ? newValue.substring(0, pos) : newValue;
|
2021-08-30 14:20:26 +00:00
|
|
|
const lastName = pos > 0 ? newValue.substring(pos + 1, newValue.length) : '';
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Clones the value for correct undo/redo
|
|
|
|
const elt = cell.value.cloneNode(true);
|
|
|
|
|
|
|
|
elt.setAttribute('firstName', firstName);
|
|
|
|
elt.setAttribute('lastName', lastName);
|
|
|
|
|
|
|
|
newValue = elt;
|
|
|
|
autoSize = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
cellLabelChanged.apply(this, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Overrides method to create the editing value
|
|
|
|
const { getEditingValue } = graph;
|
2021-08-30 14:20:26 +00:00
|
|
|
graph.getEditingValue = function (cell) {
|
2022-01-08 01:49:35 +00:00
|
|
|
if (domUtils.isNode(cell.value) && cell.value.nodeName.toLowerCase() == 'person') {
|
2021-04-25 03:39:40 +00:00
|
|
|
const firstName = cell.getAttribute('firstName', '');
|
|
|
|
const lastName = cell.getAttribute('lastName', '');
|
|
|
|
return `${firstName} ${lastName}`;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Adds a special tooltip for edges
|
|
|
|
graph.setTooltips(true);
|
|
|
|
|
|
|
|
const { getTooltipForCell } = graph;
|
2021-08-30 14:20:26 +00:00
|
|
|
graph.getTooltipForCell = function (cell) {
|
2021-04-25 03:39:40 +00:00
|
|
|
// Adds some relation details for edges
|
|
|
|
if (cell.isEdge()) {
|
|
|
|
const src = this.getLabel(cell.getTerminal(true));
|
|
|
|
const trg = this.getLabel(cell.getTerminal(false));
|
|
|
|
return `${src} ${cell.value.nodeName} ${trg}`;
|
|
|
|
}
|
|
|
|
return getTooltipForCell.apply(this, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Enables rubberband selection
|
2022-01-08 01:49:35 +00:00
|
|
|
if (args.rubberBand) new RubberBandHandler(graph);
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
const buttons = document.createElement('div');
|
|
|
|
div.appendChild(buttons);
|
|
|
|
|
|
|
|
// Adds an option to view the XML of the graph
|
|
|
|
buttons.appendChild(
|
2022-01-08 01:49:35 +00:00
|
|
|
DomHelpers.button('View XML', function () {
|
|
|
|
const encoder = new Codec();
|
|
|
|
const node = encoder.encode(graph.getDataModel());
|
2021-08-30 14:20:26 +00:00
|
|
|
popup(utils.getPrettyXml(node), true);
|
2021-04-25 03:39:40 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
// Changes the style for match the markup
|
|
|
|
// Creates the default style for vertices
|
|
|
|
let style = graph.getStylesheet().getDefaultVertexStyle();
|
2021-05-02 06:04:34 +00:00
|
|
|
style.strokeColor = 'gray';
|
|
|
|
style.rounded = true;
|
|
|
|
style.shadow = true;
|
|
|
|
style.fillColor = '#DFDFDF';
|
|
|
|
style.gradientColor = 'white';
|
2021-05-02 13:56:17 +00:00
|
|
|
style.fontColor = 'black';
|
2021-05-02 06:04:34 +00:00
|
|
|
style.fontSize = '12';
|
|
|
|
style.spacing = 4;
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Creates the default style for edges
|
|
|
|
style = graph.getStylesheet().getDefaultEdgeStyle();
|
2021-05-02 06:04:34 +00:00
|
|
|
style.strokeColor = '#0C0C0C';
|
|
|
|
style.labelBackgroundColor = 'white';
|
2021-08-30 14:20:26 +00:00
|
|
|
style.edge = EdgeStyle.ElbowConnector;
|
2021-05-02 06:04:34 +00:00
|
|
|
style.rounded = true;
|
2021-05-02 13:56:17 +00:00
|
|
|
style.fontColor = 'black';
|
2021-05-02 06:04:34 +00:00
|
|
|
style.fontSize = '10';
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Gets the default parent for inserting new cells. This
|
|
|
|
// is normally the first child of the root (ie. layer 0).
|
|
|
|
const parent = graph.getDefaultParent();
|
|
|
|
|
|
|
|
// Adds cells to the model in a single step
|
2022-01-08 01:49:35 +00:00
|
|
|
graph.batchUpdate(() => {
|
2021-04-25 03:39:40 +00:00
|
|
|
const v1 = graph.insertVertex(parent, null, person1, 40, 40, 80, 30);
|
|
|
|
const v2 = graph.insertVertex(parent, null, person2, 200, 150, 80, 30);
|
|
|
|
const e1 = graph.insertEdge(parent, null, relation, v1, v2);
|
2022-01-08 01:49:35 +00:00
|
|
|
});
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Implements a properties panel that uses
|
2021-08-30 14:20:26 +00:00
|
|
|
// CellAttributeChange to change properties
|
|
|
|
graph.getSelectionModel().addListener(InternalEvent.CHANGE, function (sender, evt) {
|
|
|
|
selectionChanged(graph);
|
|
|
|
});
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
selectionChanged(graph);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the properties panel
|
|
|
|
*/
|
|
|
|
function selectionChanged(graph) {
|
2022-05-08 09:05:22 +00:00
|
|
|
const div = document.getElementById('properties');
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Forces focusout in IE
|
|
|
|
graph.container.focus();
|
|
|
|
|
|
|
|
// Clears the DIV the non-DOM way
|
|
|
|
div.innerHTML = '';
|
|
|
|
|
|
|
|
// Gets the selection cell
|
|
|
|
const cell = graph.getSelectionCell();
|
|
|
|
|
|
|
|
if (cell == null) {
|
2022-01-08 01:49:35 +00:00
|
|
|
domUtils.writeln(div, 'Nothing selected.');
|
2021-04-25 03:39:40 +00:00
|
|
|
} else {
|
|
|
|
// Writes the title
|
|
|
|
const center = document.createElement('center');
|
2022-01-08 01:49:35 +00:00
|
|
|
domUtils.writeln(center, `${cell.value.nodeName} (${cell.id})`);
|
2021-04-25 03:39:40 +00:00
|
|
|
div.appendChild(center);
|
2022-01-08 01:49:35 +00:00
|
|
|
domUtils.br(div);
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Creates the form from the attributes of the user object
|
2022-01-08 01:49:35 +00:00
|
|
|
const form = new MaxForm();
|
2021-04-25 03:39:40 +00:00
|
|
|
const attrs = cell.value.attributes;
|
|
|
|
|
|
|
|
for (let i = 0; i < attrs.length; i++) {
|
|
|
|
createTextField(graph, form, cell, attrs[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
div.appendChild(form.getTable());
|
2022-01-08 01:49:35 +00:00
|
|
|
domUtils.br(div);
|
2021-04-25 03:39:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the textfield for the given property.
|
|
|
|
*/
|
|
|
|
function createTextField(graph, form, cell, attribute) {
|
|
|
|
const input = form.addText(`${attribute.nodeName}:`, attribute.nodeValue);
|
|
|
|
|
2021-08-30 14:20:26 +00:00
|
|
|
const applyHandler = function () {
|
2021-04-25 03:39:40 +00:00
|
|
|
const newValue = input.value || '';
|
|
|
|
const oldValue = cell.getAttribute(attribute.nodeName, '');
|
|
|
|
|
|
|
|
if (newValue != oldValue) {
|
2022-01-08 01:49:35 +00:00
|
|
|
graph.batchUpdate(() => {
|
2021-08-30 14:20:26 +00:00
|
|
|
const edit = new CellAttributeChange(cell, attribute.nodeName, newValue);
|
2022-01-08 01:49:35 +00:00
|
|
|
graph.getDataModel().execute(edit);
|
2021-04-25 03:39:40 +00:00
|
|
|
graph.updateCellSize(cell);
|
2022-01-08 01:49:35 +00:00
|
|
|
});
|
2021-04-25 03:39:40 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-08-30 14:20:26 +00:00
|
|
|
InternalEvent.addListener(input, 'keypress', function (evt) {
|
2021-04-25 03:39:40 +00:00
|
|
|
// Needs to take shift into account for textareas
|
2021-08-30 14:20:26 +00:00
|
|
|
if (evt.keyCode == /* enter */ 13 && !InternalEvent.isShiftDown(evt)) {
|
2021-04-25 03:39:40 +00:00
|
|
|
input.blur();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Note: Known problem is the blurring of fields in
|
|
|
|
// Firefox by changing the selection, in which case
|
|
|
|
// no event is fired in FF and the change is lost.
|
|
|
|
// As a workaround you should use a local variable
|
|
|
|
// that stores the focused field and invoke blur
|
|
|
|
// explicitely where we do the graph.focus above.
|
2021-08-30 14:20:26 +00:00
|
|
|
InternalEvent.addListener(input, 'blur', applyHandler);
|
2021-04-25 03:39:40 +00:00
|
|
|
}
|
|
|
|
return div;
|
2021-08-30 14:20:26 +00:00
|
|
|
};
|
2021-04-25 03:39:40 +00:00
|
|
|
|
2021-08-30 14:20:26 +00:00
|
|
|
export const Default = Template.bind({});
|