forever: constants.FOREVER_ON,
rejectUnauthorized: false,
}, function(error, response, body) {
- if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
+ if (utils.validateResponse('FileManager.job', error, response, body, resolve, reject)) {
var data = JSON.parse(response.body)['rw-pkg-mgmt:download-jobs'];
var returnData = [];
data && data.job.map(function(d) {
import EditForwardingGraphPaths from './EditorForwardingGraph/EditForwardingGraphPaths'
import SelectionManager from '../libraries/SelectionManager'
import DescriptorModelIconFactory from '../libraries/model/IconFactory'
-
import FileManager from './filemanager/FileManager.jsx';
+import ConfigPrimitiveParameters from './ConfigPrimitiveParameters/ConfigPrimitiveParameters'
import '../styles/CanvasPanel.scss'
const CanvasPanel = React.createClass({
</div>
</div>
)
+ //CanvasPanelTray panel to display
+ let displayedPanel = null;
+ switch (this.props.displayedPanel) {
+ case 'forwarding' : displayedPanel = (<EditForwardingGraphPaths containers={this.props.containers} />); break;
+ case 'parameter' : displayedPanel = (<ConfigPrimitiveParameters containers={this.props.containers} />); break;
+ default: displayedPanel = (<div><p className="welcome-message">Please select a tab</p></div>); break;
+ }
return (
<div id="canvasPanelDiv" className="CanvasPanel" style={style} onDragOver={this.onDragOver} onDrop={this.onDrop}>
<div onDoubleClick={this.onDblClickOpenFullScreen} className="CanvasPanelHeader panel-header" data-resizable="limit_bottom">
<CanvasZoom zoom={this.props.zoom} style={{bottom: this.props.layout.bottom + 20}}/>
: null
}
- <CanvasPanelTray layout={this.props.layout} show={isEditingNSD && isDescriptorView}>
- <EditForwardingGraphPaths containers={this.props.containers} />
+ <CanvasPanelTray layout={this.props.layout} displayedPanel={this.props.displayedPanel} show={isEditingNSD && isDescriptorView}>
+ {displayedPanel}
</CanvasPanelTray>
</div>
);
right: props.layout.right,
display: props.show ? false : 'none'
};
+ const PANEL = {
+ FORWARD: 'forwarding',
+ PARAMETER: 'parameter'
+ }
const classNames = ClassNames('CanvasPanelTray', {'-with-transitions': !document.body.classList.contains('resizing')});
function onClickToggleOpenClose(event) {
if (event.defaultPrevented) return;
event.preventDefault();
// don't toggle if the user was resizing
if (!uiTransient.isResizing) {
- CanvasPanelTrayActions.toggleOpenClose();
+ CanvasPanelTrayActions.toggleOpenClose(event);
}
event.target.removeEventListener('mousemove', onMouseMove, true);
}
const isOpen = style.height > 25;
return (
<div className={classNames} data-resizable="top" data-resizable-handle-offset="4" style={style}>
- <h1 data-open-close-icon={isOpen ? 'open' : 'closed'} onMouseDownCapture={onMouseDown} onClick={onClickToggleOpenClose}>Forwarding Graphs</h1>
+ <div className="CanvasPanelTray-buttons" onClick={onClickToggleOpenClose}>
+ <button
+ style={{flex: '1 1 auto'}}
+ className={ClassNames({'-selected': props.displayedPanel === PANEL.FORWARD})}
+ onMouseDownCapture={onMouseDown}
+ data-event={PANEL.FORWARD}>
+ Forwarding Graphs
+ </button>
+
+ <button
+ style={{flex: '1 1 auto', borderLeft: '1px solid white'}}
+ className={ClassNames({'-selected': props.displayedPanel === PANEL.PARAMETER})}
+ onMouseDownCapture={onMouseDown}
+ data-event={PANEL.PARAMETER}>
+ Config Parameter Map
+ </button>
+ <div data-open-close-icon={isOpen ? 'open' : 'closed'} style={{flex: '0 1', width: '40px', height: '25px', cursor: 'pointer'}} data-event='arrow'>
+
+ </div>
+ </div>
<div className="tray-body">
{props.children}
</div>
</div>
);
-}
\ No newline at end of file
+}
filesState={self.state.filesState}
item={self.state.item}
type={self.state.filterCatalogByTypeValue}
+ displayedPanel={self.state.displayedPanel}
/>
{
(self.state.panelTabShown == 'descriptor') ?
--- /dev/null
+
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * 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.
+ *
+ */
+
+
+'use strict';
+
+import d3 from 'd3'
+import React from 'react'
+import Range from '../Range'
+import Button from '../Button'
+import ClassNames from 'classnames'
+import changeCase from 'change-case'
+import LayoutRow from '../LayoutRow'
+import SelectionManager from '../../libraries/SelectionManager'
+import PureRenderMixin from 'react-addons-pure-render-mixin'
+import CatalogItemsActions from '../../actions/CatalogItemsActions'
+import CanvasEditorActions from '../../actions/CanvasEditorActions'
+import DescriptorModelFactory from '../../libraries/model/DescriptorModelFactory'
+import ComposerAppActions from '../../actions/ComposerAppActions'
+import DescriptorModelMetaFactory from '../../libraries/model/DescriptorModelMetaFactory'
+import ComposerAppStore from '../../stores/ComposerAppStore'
+import DeletionManager from '../../libraries/DeletionManager'
+import ContentEditableDiv from '../ContentEditableDiv'
+import TooltipManager from '../../libraries/TooltipManager'
+import HighlightRecordServicePaths from '../../libraries/graph/HighlightRecordServicePaths'
+
+import '../../styles/EditForwardingGraphPaths.scss'
+
+import imgNSD from '../../images/default-catalog-icon.svg'
+import imgFG from '../../../../node_modules/open-iconic/svg/infinity.svg'
+import imgRemove from '../../../../node_modules/open-iconic/svg/trash.svg'
+import imgAdd from '../../../../node_modules/open-iconic/svg/plus.svg'
+import imgConnection from '../../../../node_modules/open-iconic/svg/random.svg'
+import imgClassifier from '../../../../node_modules/open-iconic/svg/spreadsheet.svg'
+import imgReorder from '../../../../node_modules/open-iconic/svg/menu.svg'
+import EditConfigParameterMap from '../EditConfigParameterMap'
+function configParameterMapMap(ap, i) {
+
+ const context = this;
+ context.vnfapMap = ap;
+ return (
+ <div key={i}>
+ <div>{ap.id}</div>
+ <div>{ap.capability['member-vnf-index']}</div>
+ <div>{ap.capability['capability-ref']}</div>
+
+ </div>
+ )
+ /*
+ // const colors = fg.colors;
+ // const stylePrimary = {borderColor: colors.primary};
+ // const styleSecondary = {borderColor: colors.secondary};
+
+ // context.stylePrimary = stylePrimary;
+ // context.styleSecondary = styleSecondary;
+
+ // const rspMap = fg.rsp.reduce((map, rsp) => {
+ // map[rsp.id] = rsp;
+ // rsp.classifier = [];
+ // return map;
+ // }, {});
+
+ // fg.classifier.forEach(classifier => {
+ // const rsp = rspMap[classifier.model['rsp-id-ref']];
+ // if (rsp) {
+ // rsp.classifier.push(classifier);
+ // }
+ // });
+
+ // function onClickRemoveForwardingGraph(fg, event) {
+ // event.preventDefault();
+ // const root = fg.getRoot();
+ // fg.remove();
+ // CatalogItemsActions.catalogItemDescriptorChanged(root);
+ // }
+
+ // function onClickAddClassifier(context, fg, event) {
+ // event.preventDefault();
+ // fg.createClassifier();
+ // CatalogItemsActions.catalogItemDescriptorChanged(fg.getRoot());
+ // }
+
+ // function onClickToggleShowAllFGPaths(fg, event) {
+ // //event.preventDefault();
+ // event.stopPropagation();
+ // fg.uiState.showPaths = event.target.checked;
+ // fg.rsp.forEach(rsp => rsp.uiState.showPath = event.target.checked);
+ // CatalogItemsActions.catalogItemMetaDataChanged(fg.getRoot().model);
+ // }
+
+ // if (!fg.uiState.hasOwnProperty('showPaths')) {
+ // fg.uiState.showPaths = true;
+ // fg.rsp.forEach(d => d.uiState.showPath = true);
+ // }
+
+ // const toggleSelectAllPaths = (
+ // <input type="checkbox" name={'show-path' + fg.uid} checked={fg.uiState.showPaths} onChange={() => {}} onClick={onClickToggleShowAllFGPaths.bind(null, fg)} />
+ // );
+
+ // const srpFactory = DescriptorModelFactory.newRecordServicePathFactory({}, fg);
+ // srpFactory.uid = fg.uid + i;
+
+ // const hasServiceFunctionVNFDs = context.containers.filter(d => DescriptorModelFactory.isConstituentVnfdWithServiceChain(d, 'SF')).length > 0;
+
+ // return (
+ // <div key={i} className={fg.className} data-uid={fg.uid} data-offset-width="true" onClick={onClickSelectAndShowInDetailsPanel.bind(null, fg)} onCut={onCutDelegateToRemove.bind(null, fg)}>
+ // <div key="outline-indicator" data-outline-indicator="true"></div>
+ // <div className="header-actions">
+ // <Button className="remove-forwarding-graph" title="Remove" onClick={onClickRemoveForwardingGraph.bind(null, fg)} src={imgRemove}/>
+ // </div>
+ // <LayoutRow primaryActionColumn={toggleSelectAllPaths} secondaryActionColumn={<img className="fg-icon" src={imgFG} width="20px" />}>
+ // <small>{fg.title}</small>
+ // </LayoutRow>
+ // <div>
+ // <h4>Rendered Service Paths</h4>
+ // {hasServiceFunctionVNFDs ? fg.recordServicePaths.concat(srpFactory).map(mapRecordServicePath.bind(null, context)) : <small className="no-service-function-chain-msg hint">A VNFD with the chain SF is required to build Rendered Service Paths.</small>}
+ // </div>
+ // <div>
+ // <h4>Classifiers</h4>
+ // {fg.classifier.map(mapClassifier.bind(null, context))}
+ // <div className="footer-actions">
+ // <div className="row-action-column">
+ // <Button className="create-new-classifier" src={imgAdd} width="20px" onClick={onClickAddClassifier.bind(null, context, fg)} label="Add Classifier" />
+ // </div>
+ // </div>
+ // </div>
+ // </div>
+ // );
+ */
+
+}
+
+function mapNSD(nsd, i) {
+
+ const context = this;
+ context.nsd = nsd;
+
+ function onClickAddConfigParameterMap(nsd, event) {
+ event.preventDefault();
+ nsd.createConfigParameterMap();
+ CatalogItemsActions.catalogItemDescriptorChanged(nsd.getRoot());
+ }
+
+ const forwardingGraphs = nsd.configParameterMap.map(configParameterMap.bind(context));
+ if (forwardingGraphs.length === 0) {
+ forwardingGraphs.push(
+ <div key="1" className="welcome-message">
+ No Forwarding Graphs to model.
+ </div>
+ );
+ }
+
+ return (
+ <div key={i} className={nsd.className}>
+ {forwardingGraphs}
+ <div className="footer-actions">
+ <div className="row-action-column">
+ <Button className="create-new-forwarding-graph" src={imgAdd} width="20px" onClick={onClickAddConfigParameterMap.bind(null, nsd)} label="Add new Access Point" />
+ </div>
+ </div>
+ </div>
+ );
+
+}
+
+const ConfigPrimitiveParameters = React.createClass({
+ mixins: [PureRenderMixin],
+ getInitialState: function () {
+ return ComposerAppStore.getState();
+ },
+ getDefaultProps: function () {
+ return {
+ containers: []
+ };
+ },
+ componentWillMount: function () {
+ },
+ componentDidMount: function () {
+ },
+ componentDidUpdate: function () {
+ },
+ componentWillUnmount: function () {
+ },
+ render() {
+ const self = this;
+ const containers = this.props.containers;
+ const context = {
+ component: this,
+ containers: containers
+ };
+
+ const networkService = containers.filter(d => d.type === 'nsd');
+ if (networkService.length === 0) {
+ return <p className="welcome-message">No <img src={imgNSD} width="20px" /> NSD open in the canvas. Try opening an NSD.</p>;
+ }
+ return (
+ <div className="ConfigParameterMap">
+ {
+ containers.map(function(c, i) {
+ if(c.className == 'ConfigParameterMap') {
+ return <EditConfigParameterMap key={i} container={c} width={self.props.width} />
+ }
+ })
+ }
+ </div>
+ )
+ // return (
+ // <div className=" -with-transitions" data-offset-parent="true">
+ // <div key="outline-indicator" data-outline-indicator="true"></div>
+ // {containers.filter(d => d.type === 'nsd').map(mapNSD.bind(context))}
+ // </div>
+ // );
+
+ }
+});
+
+
+
+export default ConfigPrimitiveParameters;
+//<EditDescriptorModelProperties container={DescriptorModelMetaFactory.createModelInstanceForType('nsd.vnffgd.rsp')} width={this.props.width} />
--- /dev/null
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * 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.
+ *
+ */
+/**
+ * Created by onvelocity on 1/18/16.
+ *
+ * This class generates the form fields used to edit the CONFD JSON model.
+ */
+'use strict';
+
+import _ from 'lodash'
+import utils from '../libraries/utils'
+import React from 'react'
+import ClassNames from 'classnames'
+import changeCase from 'change-case'
+import toggle from '../libraries/ToggleElementHandler'
+import Button from './Button'
+import Property from '../libraries/model/DescriptorModelMetaProperty'
+import ComposerAppActions from '../actions/ComposerAppActions'
+import CatalogItemsActions from '../actions/CatalogItemsActions'
+import DESCRIPTOR_MODEL_FIELDS from '../libraries/model/DescriptorModelFields'
+import DescriptorModelFactory from '../libraries/model/DescriptorModelFactory'
+import DescriptorModelMetaFactory from '../libraries/model/DescriptorModelMetaFactory'
+import SelectionManager from '../libraries/SelectionManager'
+import DeletionManager from '../libraries/DeletionManager'
+import DescriptorModelIconFactory from '../libraries/model/IconFactory'
+import getEventPath from '../libraries/getEventPath'
+import CatalogDataStore from '../stores/CatalogDataStore'
+
+import imgAdd from '../../../node_modules/open-iconic/svg/plus.svg'
+import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
+
+import '../styles/EditDescriptorModelProperties.scss'
+import '../styles/EditConfigParameterMap.scss';
+
+
+function getDescriptorMetaBasicForType(type) {
+ const basicPropertiesFilter = d => _.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
+}
+
+function getDescriptorMetaAdvancedForType(type) {
+ const advPropertiesFilter = d => !_.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ return DescriptorModelMetaFactory.getModelMetaForType(type, advPropertiesFilter) || {properties: []};
+}
+
+function getTitle(model = {}) {
+ if (typeof model['short-name'] === 'string' && model['short-name']) {
+ return model['short-name'];
+ }
+ if (typeof model.name === 'string' && model.name) {
+ return model.name;
+ }
+ if (model.uiState && typeof model.uiState.displayName === 'string' && model.uiState.displayName) {
+ return model.uiState.displayName
+ }
+ if (typeof model.id === 'string') {
+ return model.id;
+ }
+}
+function startEditing() {
+ DeletionManager.removeEventListeners();
+ }
+
+ function endEditing() {
+ DeletionManager.addEventListeners();
+ }
+
+ function onClickSelectItem(property, path, value, event) {
+ event.preventDefault();
+ const root = this.getRoot();
+ if (SelectionManager.select(value)) {
+ CatalogItemsActions.catalogItemMetaDataChanged(root.model);
+ }
+ }
+
+ function onFocusPropertyFormInputElement(property, path, value, event) {
+
+ event.preventDefault();
+ startEditing();
+
+ function removeIsFocusedClass(event) {
+ event.target.removeEventListener('blur', removeIsFocusedClass);
+ Array.from(document.querySelectorAll('.-is-focused')).forEach(d => d.classList.remove('-is-focused'));
+ }
+
+ removeIsFocusedClass(event);
+
+ const propertyWrapper = getEventPath(event).reduce((parent, element) => {
+ if (parent) {
+ return parent;
+ }
+ if (!element.classList) {
+ return false;
+ }
+ if (element.classList.contains('property')) {
+ return element;
+ }
+ }, false);
+
+ if (propertyWrapper) {
+ propertyWrapper.classList.add('-is-focused');
+ event.target.addEventListener('blur', removeIsFocusedClass);
+ }
+
+ }
+
+ function buildAddPropertyAction(container, property, path) {
+ function onClickAddProperty(property, path, event) {
+ event.preventDefault();
+ //SelectionManager.resume();
+ const create = Property.getContainerCreateMethod(property, this);
+ if (create) {
+ const model = null;
+ create(model, path, property);
+ } else {
+ const name = path.join('.');
+ const value = Property.createModelInstance(property);
+ utils.assignPathValue(this.model, name, value);
+ }
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ return (
+ <Button className="inline-hint" onClick={onClickAddProperty.bind(container, property, path)} label="Add" src={imgAdd} />
+ );
+ }
+
+ function buildRemovePropertyAction(container, property, path) {
+ function onClickRemoveProperty(property, path, event) {
+ event.preventDefault();
+ const name = path.join('.');
+ const removeMethod = Property.getContainerMethod(property, this, 'remove');
+ if (removeMethod) {
+ removeMethod(utils.resolvePath(this.model, name));
+ } else {
+ utils.removePathValue(this.model, name);
+ }
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ return (
+ <Button className="remove-property-action inline-hint" title="Remove" onClick={onClickRemoveProperty.bind(container, property, path)} label="Remove" src={imgRemove}/>
+ );
+ }
+
+ function onFormFieldValueChanged(event) {
+ if (DescriptorModelFactory.isContainer(this)) {
+ event.preventDefault();
+ const name = event.target.name;
+ const value = JSON.parse(event.target.value);
+ utils.assignPathValue(this.model, 'config-parameter-source.config-parameter-source-ref', value.value);
+ utils.assignPathValue(this.model, 'config-parameter-source.member-vnf-index-ref', value.index);
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ }
+
+ function buildField(container, property, path, value, fieldKey, vnfdIndex) {
+ let cds = CatalogDataStore;
+ let catalogs = cds.getTransientCatalogs();
+
+ const name = path.join('.');
+ const isEditable = true;
+ const isGuid = Property.isGuid(property);
+ const onChange = onFormFieldValueChanged.bind(container);
+ const isEnumeration = Property.isEnumeration(property);
+ const isLeafRef = Property.isLeafRef(property);
+ const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+ const placeholder = changeCase.title(property.name);
+ const className = ClassNames(property.name + '-input', {'-is-guid': isGuid});
+ const fieldValue = value ? (value.constructor.name != "Object") ? value : '' : undefined;
+ // if (isEnumeration) {
+ // const enumeration = Property.getEnumeration(property, value);
+ // const options = enumeration.map((d, i) => {
+ // // note yangforge generates values for enums but the system does not use them
+ // // so we categorically ignore them
+ // // https://trello.com/c/uzEwVx6W/230-bug-enum-should-not-use-index-only-name
+ // //return <option key={fieldKey + ':' + i} value={d.value}>{d.name}</option>;
+ // return <option key={fieldKey.toString() + ':' + i} value={d.name}>{d.name}</option>;
+ // });
+ // const isValueSet = enumeration.filter(d => d.isSelected).length > 0;
+ // if (!isValueSet || property.cardinality === '0..1') {
+ // const noValueDisplayText = changeCase.title(property.name);
+ // options.unshift(<option key={'(value-not-in-enum)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ // }
+ // return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+ // }
+
+ if (isLeafRef && (path.indexOf("config-parameter-source-ref") > -1)) {
+ let fullFieldKey = _.isArray(fieldKey) ? fieldKey.join(':') : fieldKey;
+ let containerRef = container;
+ let vnfdName = null;
+ let options = [];
+ let leafRefPathValues = [];
+ while (containerRef.parent) {
+ fullFieldKey = containerRef.parent.key + ':' + fullFieldKey;
+ containerRef = containerRef.parent;
+ }
+ let parentProperty = container.parent.constituentVnfd;
+ parentProperty.map((v, i) => {
+ let somevalues = Property.getConfigParamRef(property, path, value, fullFieldKey, catalogs, container, v.vnfdId);
+ options = somevalues && options.concat(somevalues.map((d, i) => {
+ return <option key={v.vnfdId + ':' + fieldKey.toString() + ':' + i} value={JSON.stringify({value: d.value, index: v.vnfdIndex})}>{`${v['short-name']} (${v.vnfdIndex}) / ${d.value}`}</option>;
+ }))
+ leafRefPathValues = leafRefPathValues.concat(somevalues);
+
+ });
+
+ const isValueSet = leafRefPathValues.filter(d => d.isSelected).length > 0;
+ if (!isValueSet) {
+ const noValueDisplayText = changeCase.title(property.name);
+ options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ }
+ return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={JSON.stringify({value:value, index: container.model['config-parameter-source']['member-vnf-index-ref']})} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+ }
+ }
+
+ function buildElement(container, property, valuePath, value, vnfdIndex) {
+ return property.properties.map((property, index) => {
+ let childValue;
+ const childPath = valuePath.slice();
+ if (typeof value === 'object') {
+ childValue = value[property.name];
+ }
+ if(property.type != 'choice'){
+ childPath.push(property.name);
+ }
+ return build(container, property, childPath, childValue, {}, vnfdIndex);
+
+ });
+ }
+
+
+ function buildSimpleListItem(container, property, path, value, key, index) {
+ // todo need to abstract this better
+ const title = getTitle(value);
+ var req = require.context("../", true, /\.svg/);
+ return (
+ <div>
+ <a href="#select-list-item" key={Date.now()} className={property.name + '-list-item simple-list-item '} onClick={onClickSelectItem.bind(container, property, path, value)}>
+ <img src={req('./' + DescriptorModelIconFactory.getUrlForType(property.name))} width="20px" />
+ <span>{title}</span>
+ </a>
+ {buildRemovePropertyAction(container, property, path)}
+ </div>
+ );
+ }
+
+ function buildRemoveListItem(container, property, valuePath, fieldKey, index) {
+ const className = ClassNames(property.name + '-remove actions');
+ return (
+ <div key={fieldKey.concat(index).join(':')} className={className}>
+ <h3>
+ <span className={property.type + '-name name'}>{changeCase.title(property.name)}</span>
+ <span className="info">{index + 1}</span>
+ {buildRemovePropertyAction(container, property, valuePath)}
+ </h3>
+ </div>
+ );
+ }
+
+ function buildLeafListItem(container, property, valuePath, value, key, index, vnfdIndex) {
+ // look at the type to determine how to parse the value
+ return (
+ <div>
+ {buildRemoveListItem(container, property, valuePath, key, index)}
+ {buildField(container, property, valuePath, value, key, vnfdIndex)}
+ </div>
+
+ );
+ }
+
+ function build(container, property, path, value, props = {}, vnfdIndex) {
+ const fields = [];
+ const isLeaf = Property.isLeaf(property);
+ const isArray = Property.isArray(property);
+ const isObject = Property.isObject(property);
+ const isLeafList = Property.isLeafList(property);
+ const fieldKey = [container.id].concat(path);
+ const isRequired = Property.isRequired(property);
+ const title = changeCase.titleCase(property.name);
+ const columnCount = property.properties.length || 1;
+ const isColumnar = isArray && (Math.round(props.width / columnCount) > 155);
+ const classNames = {'-is-required': isRequired, '-is-columnar': isColumnar};
+
+ if (!property.properties && isObject) {
+ const uiState = DescriptorModelMetaFactory.getModelMetaForType(property.name) || {};
+ property.properties = uiState.properties;
+ }
+
+ const hasProperties = _.isArray(property.properties) && property.properties.length;
+ const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(property);
+
+ // ensure value is not undefined for non-leaf property types
+ if (isObject) {
+ if (typeof value !== 'object') {
+ value = isArray ? [] : {};
+ }
+ }
+ const valueAsArray = _.isArray(value) ? value : isLeafList && typeof value === 'undefined' ? [] : [value];
+
+ const isMetaField = property.name === 'meta';
+ const isCVNFD = property.name === 'constituent-vnfd';
+ const isSimpleListView = Property.isSimpleList(property);
+
+ valueAsArray.forEach((value, index) => {
+
+ let field;
+ const key = fieldKey.slice();
+ const valuePath = path.slice();
+
+ if (isArray) {
+ valuePath.push(index);
+ key.push(index);
+ }
+
+ if (isMetaField) {
+ if (typeof value === 'object') {
+ value = JSON.stringify(value, undefined, 12);
+ } else if (typeof value !== 'string') {
+ value = '{}';
+ }
+ }
+
+ if (isMissingDescriptorMeta) {
+ field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
+ } else if (property.type === 'choice') {
+ field = buildChoice(container, property, valuePath, value, key.join(':'), props);
+ } else if (isSimpleListView) {
+ field = buildSimpleListItem(container, property, valuePath, value, key, index, props);
+ } else if (isLeafList) {
+ field = buildLeafListItem(container, property, valuePath, value, key, index, vnfdIndex);
+ } else if (hasProperties) {
+ field = buildElement(container, property, valuePath, value, vnfdIndex)
+ } else {
+ field = buildField(container, property, valuePath, value, key.join(':'), vnfdIndex);
+ }
+
+ function onClickLeaf(property, path, value, event) {
+ if (event.isDefaultPrevented()) {
+ return;
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ this.getRoot().uiState.focusedPropertyPath = path.join('.');
+ console.log('property selected', path.join('.'));
+ ComposerAppActions.propertySelected([path.join('.')]);
+ }
+
+ const clickHandler = isLeaf ? onClickLeaf : () => {};
+ const isContainerList = isArray && !(isSimpleListView || isLeafList);
+ if(valuePath.indexOf("member-vnf-index-ref") == -1) {
+ fields.push(
+ <div key={fieldKey.concat(['property-content', index]).join(':')}
+ className={ClassNames('property-content', {'simple-list': isSimpleListView})}
+ onClick={clickHandler.bind(container, property, valuePath, value)}>
+ {isContainerList ? buildRemoveListItem(container, property, valuePath, fieldKey, index) : null}
+ {field}
+ </div>
+ );
+ }
+ });
+
+ classNames['-is-leaf'] = isLeaf;
+ classNames['-is-array'] = isArray;
+ classNames['cols-' + columnCount] = isColumnar;
+
+ if (property.type === 'choice') {
+ value = utils.resolvePath(container.model, ['uiState.choice'].concat(path, 'selected').join('.'));
+ if(!value) {
+ property.properties.map(function(p) {
+ let pname = p.properties[0].name;
+ if(container.model.hasOwnProperty(pname)) {
+ value = container.model[pname];
+ }
+ })
+ }
+ }
+
+ let displayValue = typeof value === 'object' ? '' : value;
+ const displayValueInfo = isArray ? valueAsArray.filter(d => typeof d !== 'undefined').length + ' items' : '';
+
+ const onFocus = isLeaf ? event => event.target.classList.add('-is-focused') : false;
+
+ //Remove first entry, which is member-vnf-index because I'm tired of trying to figure out how it's being added and feel pressed for time. Come back here if there's ever time and fix correctly.
+ if (fieldKey.indexOf('member-vnf-index-ref') == -1) {
+ return (
+ <div key={fieldKey.join(':')} onFocus={onFocus}>
+ {fields}
+ </div>
+ );
+ }
+ }
+export default function EditDescriptorModelProperties(props, type) {
+
+ const container = props.container;
+
+ if (!(DescriptorModelFactory.isContainer(container))) {
+ return
+ }
+
+
+
+ const containerType = (_.isEmpty(type) ? false : type)|| container.uiState['qualified-type'] || container.uiState.type;
+ const basicProperties = getDescriptorMetaBasicForType(containerType).properties;
+
+
+ function buildAdvancedGroup() {
+ const properties = getDescriptorMetaAdvancedForType(containerType).properties;
+ if (properties.length === 0) {
+ return null;
+ }
+ const hasBasicFields = basicProperties.length > 0;
+ const closeGroup = basicProperties.length > 0;
+ return (
+ <div className="config-parameter-map">
+ <div className="config-parameter">
+ Request
+ </div>
+ <div className="config-parameter">
+ Source
+ </div>
+ {properties.map((property,i) => {
+ const path = [property.name];
+ const value = container.model[property.name];
+ if(path == 'id') {
+ return null
+ }
+ if(path == 'config-parameter-request') {
+ let cds = CatalogDataStore;
+ let catalogs = cds.getTransientCatalogs();
+ let vnfdIndexRef = container.model[path]['member-vnf-index-ref'];
+ let vnfdIdRef = container.parent.model['constituent-vnfd'].filter(function(v){return v['member-vnf-index'] == vnfdIndexRef})[0]['vnfd-id-ref'];
+ let vnfd = catalogs[1].descriptors.filter((v) => v.id == vnfdIdRef)[0];
+ let primitiveName = vnfd['config-parameter']['config-parameter-request'].filter((p) => p.name == value['config-parameter-request-ref'] )[0].parameter[0]['config-primitive-name-ref'];
+ return (
+ <div className="config-parameter config-parameter-request" key={path + '-' + i}>
+ {`${vnfd['short-name']}(${vnfdIndexRef}) / ${primitiveName} / ${value['config-parameter-request-ref']}`}
+ </div>
+ )
+ } else if(path == 'config-parameter-source') {
+ //Builds Source
+ return <div className="config-parameter config-parameter-source"> {build(container, property, path, value, _.assign({toggle: true, width: props.width}, props), value['member-vnf-index-ref'])} </div>
+ }
+
+ })}
+ <div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
+ </div>
+ );
+ }
+
+ function buildMoreLess(d, i) {
+ return (
+ <span key={'bread-crumb-part-' + i}>
+ <a href="#select-item" onClick={onClickSelectItem.bind(d, null, null, d)}>{d.title}</a>
+ <i> / </i>
+ </span>
+ );
+ }
+
+ const path = [];
+ if (container.parent) {
+ path.push(container.parent);
+ }
+ path.push(container);
+
+ return (
+ <div className="EditDescriptorModelProperties -is-tree-view">
+ {buildAdvancedGroup()}
+ </div>
+ );
+
+}
+export {build}
+// export buildElement;
+// export buildChoice;
import '../styles/EditDescriptorModelProperties.scss'
+
+
function getDescriptorMetaBasicForType(type) {
const basicPropertiesFilter = d => _.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
return model.id;
}
}
-
-export default function EditDescriptorModelProperties(props) {
-
- const container = props.container;
-
- if (!(DescriptorModelFactory.isContainer(container))) {
- return
- }
-
- function startEditing() {
+function startEditing() {
DeletionManager.removeEventListeners();
}
});
}
- function buildChoice(container, property, path, value, key) {
+ function buildChoice(container, property, path, value, key, props={}) {
function onFormFieldValueChanged(event) {
if (DescriptorModelFactory.isContainer(this)) {
if (isMissingDescriptorMeta) {
field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
} else if (property.type === 'choice') {
- field = buildChoice(container, property, valuePath, value, key.join(':'));
+ field = buildChoice(container, property, valuePath, value, key.join(':'), props);
} else if (isSimpleListView) {
- field = buildSimpleListItem(container, property, valuePath, value, key, index);
+ field = buildSimpleListItem(container, property, valuePath, value, key, index, props);
} else if (isLeafList) {
- field = buildLeafListItem(container, property, valuePath, value, key, index);
+ field = buildLeafListItem(container, property, valuePath, value, key, index, props);
} else if (hasProperties) {
- field = buildElement(container, property, valuePath, value, key.join(':'))
+ field = buildElement(container, property, valuePath, value, key.join(':'), props)
} else {
- field = buildField(container, property, valuePath, value, key.join(':'));
+ field = buildField(container, property, valuePath, value, key.join(':'), props);
}
function onClickLeaf(property, path, value, event) {
);
}
+export default function EditDescriptorModelProperties(props, type) {
+
+ const container = props.container;
+
+ if (!(DescriptorModelFactory.isContainer(container))) {
+ return
+ }
+
+
- const containerType = container.uiState['qualified-type'] || container.uiState.type;
+ const containerType = (_.isEmpty(type) ? false : type)|| container.uiState['qualified-type'] || container.uiState.type;
const basicProperties = getDescriptorMetaBasicForType(containerType).properties;
function buildBasicGroup() {
{properties.map(property => {
const path = [property.name];
const value = container.model[property.name];
- return build(container, property, path, value, {toggle: true, width: props.width});
+ return build(container, property, path, value, _.assign({toggle: true, width: props.width}, props));
})}
</div>
<div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
);
}
+export {build}
+// export buildElement;
+// export buildChoice;
--- /dev/null
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * 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.
+ *
+ */
+/**
+ * Created by onvelocity on 1/18/16.
+ *
+ * This class generates the form fields used to edit the CONFD JSON model.
+ */
+'use strict';
+
+import _ from 'lodash'
+import utils from '../libraries/utils'
+import React from 'react'
+import ClassNames from 'classnames'
+import changeCase from 'change-case'
+import toggle from '../libraries/ToggleElementHandler'
+import Button from './Button'
+import Property from '../libraries/model/DescriptorModelMetaProperty'
+import ComposerAppActions from '../actions/ComposerAppActions'
+import CatalogItemsActions from '../actions/CatalogItemsActions'
+import DESCRIPTOR_MODEL_FIELDS from '../libraries/model/DescriptorModelFields'
+import DescriptorModelFactory from '../libraries/model/DescriptorModelFactory'
+import DescriptorModelMetaFactory from '../libraries/model/DescriptorModelMetaFactory'
+import SelectionManager from '../libraries/SelectionManager'
+import DeletionManager from '../libraries/DeletionManager'
+import DescriptorModelIconFactory from '../libraries/model/IconFactory'
+import getEventPath from '../libraries/getEventPath'
+import CatalogDataStore from '../stores/CatalogDataStore'
+
+import imgAdd from '../../../node_modules/open-iconic/svg/plus.svg'
+import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
+
+import '../styles/EditDescriptorModelProperties.scss'
+
+
+
+function getDescriptorMetaBasicForType(type) {
+ const basicPropertiesFilter = d => _.contains(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
+}
+
+function getDescriptorMetaAdvancedForType(type) {
+ const advPropertiesFilter = d => !_.contains(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ return DescriptorModelMetaFactory.getModelMetaForType(type, advPropertiesFilter) || {properties: []};
+}
+
+function getTitle(model = {}) {
+ if (typeof model['short-name'] === 'string' && model['short-name']) {
+ return model['short-name'];
+ }
+ if (typeof model.name === 'string' && model.name) {
+ return model.name;
+ }
+ if (model.uiState && typeof model.uiState.displayName === 'string' && model.uiState.displayName) {
+ return model.uiState.displayName
+ }
+ if (typeof model.id === 'string') {
+ return model.id;
+ }
+}
+
+export default function EditDescriptorModelProperties(props) {
+
+ const container = props.container;
+
+ if (!(DescriptorModelFactory.isContainer(container))) {
+ return
+ }
+
+ function startEditing() {
+ DeletionManager.removeEventListeners();
+ }
+
+ function endEditing() {
+ DeletionManager.addEventListeners();
+ }
+
+ function onClickSelectItem(property, path, value, event) {
+ event.preventDefault();
+ const root = this.getRoot();
+ if (SelectionManager.select(value)) {
+ CatalogItemsActions.catalogItemMetaDataChanged(root.model);
+ }
+ }
+
+ function onFocusPropertyFormInputElement(property, path, value, event) {
+
+ event.preventDefault();
+ startEditing();
+
+ function removeIsFocusedClass(event) {
+ event.target.removeEventListener('blur', removeIsFocusedClass);
+ Array.from(document.querySelectorAll('.-is-focused')).forEach(d => d.classList.remove('-is-focused'));
+ }
+
+ removeIsFocusedClass(event);
+
+ const propertyWrapper = getEventPath(event).reduce((parent, element) => {
+ if (parent) {
+ return parent;
+ }
+ if (!element.classList) {
+ return false;
+ }
+ if (element.classList.contains('property')) {
+ return element;
+ }
+ }, false);
+
+ if (propertyWrapper) {
+ propertyWrapper.classList.add('-is-focused');
+ event.target.addEventListener('blur', removeIsFocusedClass);
+ }
+
+ }
+
+ function buildAddPropertyAction(container, property, path) {
+ function onClickAddProperty(property, path, event) {
+ event.preventDefault();
+ //SelectionManager.resume();
+ const create = Property.getContainerCreateMethod(property, this);
+ if (create) {
+ const model = null;
+ create(model, path, property);
+ } else {
+ const name = path.join('.');
+ const value = Property.createModelInstance(property);
+ utils.assignPathValue(this.model, name, value);
+ }
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ return (
+ <Button className="inline-hint" onClick={onClickAddProperty.bind(container, property, path)} label="Add" src={imgAdd} />
+ );
+ }
+
+ function buildRemovePropertyAction(container, property, path) {
+ function onClickRemoveProperty(property, path, event) {
+ event.preventDefault();
+ const name = path.join('.');
+ const removeMethod = Property.getContainerMethod(property, this, 'remove');
+ if (removeMethod) {
+ removeMethod(utils.resolvePath(this.model, name));
+ } else {
+ utils.removePathValue(this.model, name);
+ }
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ return (
+ <Button className="remove-property-action inline-hint" title="Remove" onClick={onClickRemoveProperty.bind(container, property, path)} label="Remove" src={imgRemove}/>
+ );
+ }
+
+ function onFormFieldValueChanged(event) {
+ if (DescriptorModelFactory.isContainer(this)) {
+ event.preventDefault();
+ const name = event.target.name;
+ const value = event.target.value;
+ utils.assignPathValue(this.model, name, value);
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ }
+
+ function buildField(container, property, path, value, fieldKey) {
+ let cds = CatalogDataStore;
+ let catalogs = cds.getTransientCatalogs();
+
+ const name = path.join('.');
+ const isEditable = true;
+ const isGuid = Property.isGuid(property);
+ const onChange = onFormFieldValueChanged.bind(container);
+ const isEnumeration = Property.isEnumeration(property);
+ const isLeafRef = Property.isLeafRef(property);
+ const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+ const placeholder = changeCase.title(property.name);
+ const className = ClassNames(property.name + '-input', {'-is-guid': isGuid});
+ const fieldValue = value ? (value.constructor.name != "Object") ? value : '' : undefined;
+ if (isEnumeration) {
+ const enumeration = Property.getEnumeration(property, value);
+ const options = enumeration.map((d, i) => {
+ // note yangforge generates values for enums but the system does not use them
+ // so we categorically ignore them
+ // https://trello.com/c/uzEwVx6W/230-bug-enum-should-not-use-index-only-name
+ //return <option key={fieldKey + ':' + i} value={d.value}>{d.name}</option>;
+ return <option key={fieldKey.toString() + ':' + i} value={d.name}>{d.name}</option>;
+ });
+ const isValueSet = enumeration.filter(d => d.isSelected).length > 0;
+ if (!isValueSet || property.cardinality === '0..1') {
+ const noValueDisplayText = changeCase.title(property.name);
+ options.unshift(<option key={'(value-not-in-enum)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ }
+ return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+ }
+
+ if (isLeafRef) {
+ let fullFieldKey = fieldKey;
+ let containerRef = container;
+ while (containerRef.parent) {
+ fullFieldKey = containerRef.parent.key + ':' + fullFieldKey;
+ containerRef = containerRef.parent;
+ }
+ const leafRefPathValues = Property.getLeafRef(property, path, value, fullFieldKey, catalogs, container);
+
+ const options = leafRefPathValues && leafRefPathValues.map((d, i) => {
+ return <option key={fieldKey.toString() + ':' + i} value={d.value}>{d.value}</option>;
+ });
+ const isValueSet = leafRefPathValues.filter(d => d.isSelected).length > 0;
+ if (!isValueSet || property.cardinality === '0..1') {
+ const noValueDisplayText = changeCase.title(property.name);
+ options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ }
+ return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+ }
+
+ if (property['preserve-line-breaks']) {
+ return <textarea key={fieldKey.toString()} cols="5" id={fieldKey.toString()} name={name} value={value} placeholder={placeholder} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!isEditable} />;
+ }
+
+ return <input key={fieldKey.toString()}
+ id={fieldKey.toString()}
+ type="text"
+ name={name}
+ value={fieldValue}
+ className={className}
+ placeholder={placeholder}
+ onChange={onChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
+ readOnly={!isEditable}
+ />;
+
+ }
+
+ function buildElement(container, property, valuePath, value) {
+ return property.properties.map((property, index) => {
+ let childValue;
+ const childPath = valuePath.slice();
+ if (typeof value === 'object') {
+ childValue = value[property.name];
+ }
+ if(property.type != 'choice'){
+ childPath.push(property.name);
+ }
+ return build(container, property, childPath, childValue);
+
+ });
+ }
+
+ function buildChoice(container, property, path, value, key) {
+
+ function onFormFieldValueChanged(event) {
+ if (DescriptorModelFactory.isContainer(this)) {
+
+ event.preventDefault();
+
+ let name = event.target.name;
+ const value = event.target.value;
+
+
+ /*
+ Transient State is stored for convenience in the uiState field.
+ The choice yang type uses case elements to describe the "options".
+ A choice can only ever have one option selected which allows
+ the system to determine which type is selected by the name of
+ the element contained within the field.
+ */
+ /*
+ const stateExample = {
+ uiState: {
+ choice: {
+ 'conf-config': {
+ selected: 'rest',
+ 'case': {
+ rest: {},
+ netconf: {},
+ script: {}
+ }
+ }
+ }
+ }
+ };
+ */
+ const statePath = ['uiState.choice'].concat(name);
+ const stateObject = utils.resolvePath(this.model, statePath.join('.')) || {};
+ const selected = stateObject.selected ? stateObject.selected.split('.')[1] : undefined;
+ // write state back to the model so the new state objects are captured
+ utils.assignPathValue(this.model, statePath.join('.'), stateObject);
+
+ // write the current choice value into the state
+ let choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
+ let isTopCase = false;
+ if (!choiceObject) {
+ isTopCase = true;
+ choiceObject = utils.resolvePath(this.model, [selected].join('.'));
+ }
+ utils.assignPathValue(stateObject, [selected].join('.'), _.cloneDeep(choiceObject));
+
+ if(selected) {
+ if(this.model.uiState.choice.hasOwnProperty(name)) {
+ delete this.model[selected];
+ utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+ } else {
+ // remove the current choice value from the model
+ utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+ }
+ }
+
+ // get any state for the new selected choice
+ const newChoiceObject = utils.resolvePath(stateObject, [value].join('.')) || {};
+
+ // assign new choice value to the model
+ if (isTopCase) {
+ utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+ } else {
+ utils.assignPathValue(this.model, [value].join('.'), newChoiceObject)
+ }
+
+
+ // update the selected name
+ utils.assignPathValue(this.model, statePath.concat('selected').join('.'), value);
+
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ }
+
+ const caseByNameMap = {};
+
+ const onChange = onFormFieldValueChanged.bind(container);
+
+ const cases = property.properties.map(d => {
+ if (d.type === 'case') {
+ caseByNameMap[d.name] = d.properties[0];
+ return {
+ optionName: d.name,
+ optionTitle: d.description,
+ //represents case name and case element name
+ optionValue: [d.name, d.properties[0].name].join('.')
+ };
+ }
+ caseByNameMap[d.name] = d;
+ return {optionName: d.name};
+ });
+
+ const options = [{optionName: '', optionValue: false}].concat(cases).map((d, i) => {
+ return (
+ <option key={i} value={d.optionValue} title={d.optionTitle}>
+ {d.optionName}
+ {i ? null : changeCase.title(property.name)}
+ </option>
+ );
+ });
+
+ const selectName = path.join('.');
+ let selectedOptionPath = ['uiState.choice', selectName, 'selected'].join('.');
+ //Currently selected choice/case statement on UI model
+ let selectedOptionValue = utils.resolvePath(container.model, selectedOptionPath);
+ //If first time loaded, and none is selected, check if there is a value corresponding to a case statement in the container model
+ if(!selectedOptionValue) {
+ //get field properties for choice on container model
+ let fieldProperties = utils.resolvePath(container.model, selectName);
+ if(fieldProperties) {
+ //Check each case statement in model and see if it is present in container model.
+ cases.map(function(c){
+ if(fieldProperties.hasOwnProperty(c.optionValue.split('.')[1])) {
+ utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), c.optionValue);
+ }
+ });
+ selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+ } else {
+ property.properties.map(function(p) {
+ let pname = p.properties[0].name;
+ if(container.model.hasOwnProperty(pname)) {
+ utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), [p.name, pname].join('.'));
+ }
+ })
+ selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+ }
+ }
+ //If selectedOptionValue is present, take first item in string which represents the case name.
+ const valueProperty = caseByNameMap[selectedOptionValue ? selectedOptionValue.split('.')[0] : undefined] || {properties: []};
+ const isLeaf = Property.isLeaf(valueProperty);
+ const hasProperties = _.isArray(valueProperty.properties) && valueProperty.properties.length;
+ const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(valueProperty);
+ //Some magic that prevents errors for arising
+ const valueResponse = valueProperty.properties.length ? valueProperty.properties.map((d, i) => {
+ const childPath = path.concat(valueProperty.name, d.name);
+ const childValue = utils.resolvePath(container.model, childPath.join('.'));
+ return (
+ <div key={childPath.concat('info', i).join(':')}>
+ {build(container, d, childPath, childValue, props)}
+ </div>
+ );
+ }) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name]) : null
+ // end magic
+ const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+
+ return (
+ <div key={key} className="choice">
+ <select key={Date.now()} className={ClassNames({'-value-not-set': !selectedOptionValue})} name={selectName} value={selectedOptionValue} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing}>
+ {options}
+ </select>
+ {valueResponse}
+ </div>
+ );
+
+ }
+
+ function buildSimpleListItem(container, property, path, value, key, index) {
+ // todo need to abstract this better
+ const title = getTitle(value);
+ var req = require.context("../", true, /\.svg/);
+ return (
+ <div>
+ <a href="#select-list-item" key={Date.now()} className={property.name + '-list-item simple-list-item '} onClick={onClickSelectItem.bind(container, property, path, value)}>
+ <img src={req('./' + DescriptorModelIconFactory.getUrlForType(property.name))} width="20px" />
+ <span>{title}</span>
+ </a>
+ {buildRemovePropertyAction(container, property, path)}
+ </div>
+ );
+ }
+
+ function buildRemoveListItem(container, property, valuePath, fieldKey, index) {
+ const className = ClassNames(property.name + '-remove actions');
+ return (
+ <div key={fieldKey.concat(index).join(':')} className={className}>
+ <h3>
+ <span className={property.type + '-name name'}>{changeCase.title(property.name)}</span>
+ <span className="info">{index + 1}</span>
+ {buildRemovePropertyAction(container, property, valuePath)}
+ </h3>
+ </div>
+ );
+ }
+
+ function buildLeafListItem(container, property, valuePath, value, key, index) {
+ // look at the type to determine how to parse the value
+ return (
+ <div>
+ {buildRemoveListItem(container, property, valuePath, key, index)}
+ {buildField(container, property, valuePath, value, key)}
+ </div>
+
+ );
+ }
+
+ function build(container, property, path, value, props = {}) {
+
+ const fields = [];
+ const isLeaf = Property.isLeaf(property);
+ const isArray = Property.isArray(property);
+ const isObject = Property.isObject(property);
+ const isLeafList = Property.isLeafList(property);
+ const fieldKey = [container.id].concat(path);
+ const isRequired = Property.isRequired(property);
+ const title = changeCase.titleCase(property.name);
+ const columnCount = property.properties.length || 1;
+ const isColumnar = isArray && (Math.round(props.width / columnCount) > 155);
+ const classNames = {'-is-required': isRequired, '-is-columnar': isColumnar};
+
+ if (!property.properties && isObject) {
+ const uiState = DescriptorModelMetaFactory.getModelMetaForType(property.name) || {};
+ property.properties = uiState.properties;
+ }
+
+ const hasProperties = _.isArray(property.properties) && property.properties.length;
+ const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(property);
+
+ // ensure value is not undefined for non-leaf property types
+ if (isObject) {
+ if (typeof value !== 'object') {
+ value = isArray ? [] : {};
+ }
+ }
+ const valueAsArray = _.isArray(value) ? value : isLeafList && typeof value === 'undefined' ? [] : [value];
+
+ const isMetaField = property.name === 'meta';
+ const isCVNFD = property.name === 'constituent-vnfd';
+ const isSimpleListView = Property.isSimpleList(property);
+
+ valueAsArray.forEach((value, index) => {
+
+ let field;
+ const key = fieldKey.slice();
+ const valuePath = path.slice();
+
+ if (isArray) {
+ valuePath.push(index);
+ key.push(index);
+ }
+
+ if (isMetaField) {
+ if (typeof value === 'object') {
+ value = JSON.stringify(value, undefined, 12);
+ } else if (typeof value !== 'string') {
+ value = '{}';
+ }
+ }
+
+ if (isMissingDescriptorMeta) {
+ field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
+ } else if (property.type === 'choice') {
+ field = buildChoice(container, property, valuePath, value, key.join(':'));
+ } else if (isSimpleListView) {
+ field = buildSimpleListItem(container, property, valuePath, value, key, index);
+ } else if (isLeafList) {
+ field = buildLeafListItem(container, property, valuePath, value, key, index);
+ } else if (hasProperties) {
+ field = buildElement(container, property, valuePath, value, key.join(':'))
+ } else {
+ field = buildField(container, property, valuePath, value, key.join(':'));
+ }
+
+ function onClickLeaf(property, path, value, event) {
+ if (event.isDefaultPrevented()) {
+ return;
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ this.getRoot().uiState.focusedPropertyPath = path.join('.');
+ console.log('property selected', path.join('.'));
+ ComposerAppActions.propertySelected([path.join('.')]);
+ }
+
+ const clickHandler = isLeaf ? onClickLeaf : () => {};
+ const isContainerList = isArray && !(isSimpleListView || isLeafList);
+
+ fields.push(
+ <div key={fieldKey.concat(['property-content', index]).join(':')}
+ className={ClassNames('property-content', {'simple-list': isSimpleListView})}
+ onClick={clickHandler.bind(container, property, valuePath, value)}>
+ {isContainerList ? buildRemoveListItem(container, property, valuePath, fieldKey, index) : null}
+ {field}
+ </div>
+ );
+
+ });
+
+ classNames['-is-leaf'] = isLeaf;
+ classNames['-is-array'] = isArray;
+ classNames['cols-' + columnCount] = isColumnar;
+
+ if (property.type === 'choice') {
+ value = utils.resolvePath(container.model, ['uiState.choice'].concat(path, 'selected').join('.'));
+ if(!value) {
+ property.properties.map(function(p) {
+ let pname = p.properties[0].name;
+ if(container.model.hasOwnProperty(pname)) {
+ value = container.model[pname];
+ }
+ })
+ }
+ }
+
+ let displayValue = typeof value === 'object' ? '' : value;
+ const displayValueInfo = isArray ? valueAsArray.filter(d => typeof d !== 'undefined').length + ' items' : '';
+
+ const onFocus = isLeaf ? event => event.target.classList.add('-is-focused') : false;
+
+ return (
+ <div key={fieldKey.join(':')} className={ClassNames(property.type + '-property property', classNames)} onFocus={onFocus}>
+ <h3 className="property-label">
+ <label htmlFor={fieldKey.join(':')}>
+ <span className={property.type + '-name name'}>{title}</span>
+ <small>
+ <span className={property.type + '-info info'}>{displayValueInfo}</span>
+ <span className={property.type + '-value value'}>{displayValue}</span>
+ </small>
+ {isArray ? buildAddPropertyAction(container, property, path.concat(valueAsArray.length)) : null}
+ </label>
+ </h3>
+ <span className={property.type + '-description description'}>{property.description}</span>
+ <val className="property-value">
+ {isCVNFD ? <span className={property.type + '-tip tip'}>Drag a VNFD from the Catalog to add more.</span> : null}
+ {fields}
+ </val>
+ </div>
+ );
+
+ }
+
+ const containerType = container.uiState['qualified-type'] || container.uiState.type;
+ const basicProperties = getDescriptorMetaBasicForType(containerType).properties;
+
+ function buildBasicGroup() {
+ if (basicProperties.length === 0) {
+ return null;
+ }
+ return (
+ <div className="basic-properties-group">
+ <h2>Basic</h2>
+ <div>
+ {basicProperties.map(property => {
+ const path = [property.name];
+ const value = container.model[property.name];
+ return build(container, property, path, value);
+ })}
+ </div>
+ </div>
+ );
+ }
+
+ function buildAdvancedGroup() {
+ const properties = getDescriptorMetaAdvancedForType(containerType).properties;
+ if (properties.length === 0) {
+ return null;
+ }
+ const hasBasicFields = basicProperties.length > 0;
+ const closeGroup = basicProperties.length > 0;
+ return (
+ <div className="advanced-properties-group">
+ <h1 data-toggle={closeGroup ? 'true' : 'false'} className={ClassNames({'-is-toggled': closeGroup})} onClick={toggle} style={{display: hasBasicFields ? 'block' : 'none'}}>
+ <a className="toggle-show-more" href="#show-more-properties">more…</a>
+ <a className="toggle-show-less" href="#show-more-properties">less…</a>
+ </h1>
+ <div className="toggleable">
+ {properties.map(property => {
+ const path = [property.name];
+ const value = container.model[property.name];
+ return build(container, property, path, value, {toggle: true, width: props.width});
+ })}
+ </div>
+ <div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
+ </div>
+ );
+ }
+
+ function buildMoreLess(d, i) {
+ return (
+ <span key={'bread-crumb-part-' + i}>
+ <a href="#select-item" onClick={onClickSelectItem.bind(d, null, null, d)}>{d.title}</a>
+ <i> / </i>
+ </span>
+ );
+ }
+
+ const path = [];
+ if (container.parent) {
+ path.push(container.parent);
+ }
+ path.push(container);
+
+ return (
+ <div className="EditDescriptorModelProperties -is-tree-view">
+ <h1>{path.map(buildMoreLess)}</h1>
+ {buildBasicGroup()}
+ {buildAdvancedGroup()}
+ </div>
+ );
+
+}
+// export buildElement;
+// export buildChoice;
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
import InternalConnectionPointRef from './descriptors/InternalConnectionPointRef'
import VirtualNetworkFunctionConnectionPoint from './descriptors/VirtualNetworkFunctionConnectionPoint'
import VirtualDeploymentUnitInternalConnectionPoint from './descriptors/VirtualDeploymentUnitInternalConnectionPoint'
-
+import VirtualNetworkFunctionAccessPointMap from './descriptors/VirtualNetworkFunctionAccessPointMap'
function findChildDescriptorModelAndUpdateModel(model, parent) {
if (parent instanceof DescriptorModel) {
const child = parent.findChildByUid(model);
fg.rsp.forEach(rsp => mapRSP(rsp, containerList));
fg.classifier.forEach(classifier => mapClassifier(classifier, containerList));
}
+ function mapConfigParameterMap(ap, containerList) {
+ containerList.push(ap);
+ }
function mapVDU(vdu, containerList) {
containerList.push(vdu);
nsd.constituentVnfd.forEach(cvnfd => mapCVNFD(cvnfd, containerList));
nsd.vld.forEach(vld => mapVLD(vld, containerList));
nsd.vnffgd.forEach(fg => mapFG(fg, containerList));
+ nsd.configParameterMap.forEach(ap => mapConfigParameterMap(ap, containerList));
}
function mapVNFD(vnfd, containerList) {
return findChildDescriptorModelAndUpdateModel(model, parent) || new VnfdConnectionPointRef(model, parent);
}
+ static newVirtualNetworkFunctionAccessPointMap(model, parent) {
+ return findChildDescriptorModelAndUpdateModel(model, parent) || new VirtualNetworkFunctionAccessPointMap(model, parent);
+ }
static newForwardingGraph(model, parent) {
return findChildDescriptorModelAndUpdateModel(model, parent) || new ForwardingGraph(model, parent);
}
return obj instanceof VirtualNetworkFunction;
}
+ static isVirtualNetworkFunctionAccessPointMap(obj) {
+ return obj instanceof VirtualNetworkFunctionAccessPointMap;
+ }
static isForwardingGraph(obj) {
return obj instanceof ForwardingGraph;
}
return NetworkService;
}
+ static get VirtualNetworkFunctionAccessPointMap () {
+ return VirtualNetworkFunctionAccessPointMap;
+ }
static get ForwardingGraph () {
return ForwardingGraph;
}
vld: common.concat([]),
vnfd: common.concat(['vdu', 'internal-vld']),
'vnfd.vdu': common.concat(['image', 'external-interface', 'vm-flavor', 'cloud-init', 'filename']),
+ 'nsd.config-parameter-map': common.concat([]),
// white-list valid fields to send in the meta field
meta: ['containerPositionMap']
};
const exportInnerTypesMap = {
'constituent-vnfd': 'nsd.constituent-vnfd',
+ 'config-parameter-map': 'nsd.config-parameter-map',
'vdu': 'vnfd.vdu'
};
return leafRefObjects;
},
+
+ getConfigParamRef(property = {}, path, value, fullFieldKey, transientCatalogs, container, vnfdId) {
+ // const leafRefPath = property['data-type']['leafref']['path'];
+ const leafRefPath = "/vnfd:vnfd-catalog/vnfd:vnfd[vnfd:id = " + vnfdId + "]/vnfd:config-parameter/vnfd:config-parameter-source/vnfd:name"
+ const transientCatalogHash = {};
+
+ transientCatalogs.map((catalog) => {
+ transientCatalogHash[catalog.type + '-catalog'] = {};
+ transientCatalogHash[catalog.type + '-catalog'][catalog.type] = catalog['descriptors'];
+ });
+
+ let leafRefPathValues = utils.resolveLeafRefPath(transientCatalogHash, leafRefPath, fullFieldKey, path, container);
+
+ let leafRefObjects = [];
+
+ leafRefPathValues && leafRefPathValues.map((leafRefPathValue) => {
+ leafRefObjects.push({
+ name: leafRefPathValue,
+ value: leafRefPathValue,
+ isSelected: String(leafRefPathValue) === String(value)
+ });
+ });
+
+ return leafRefObjects;
+ },
isGuid(property = {}) {
const type = property['data-type'];
if (typeof type === 'object' && type.leafref && type.leafref.path) {
let vldFields = null;
let vnfdFields = null;
let cvnfdFields = null;
+let configParameterMapFields = null;
const confd = _.omit(copy, ['uiState']);
return cleanEmptyTopKeys(confd);
}
+ },
+ 'config-parameter-map': {
+ serialize(configParameterMap) {
+ //vnfapMapFields
+ if(!configParameterMapFields) configParameterMapFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd.config-parameter-map');
+ return _.pick(configParameterMap, configParameterMapFields);
+ }
}
};
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
import ColorGroups from '../../ColorGroups'
import DescriptorModel from '../DescriptorModel'
import ForwardingGraph from './ForwardingGraph'
+import VirtualNetworkFunctionAccessPointMap from './VirtualNetworkFunctionAccessPointMap'
import VirtualLink from './VirtualLink'
import ConstituentVnfd from './ConstituentVnfd'
import PhysicalNetworkFunction from './PhysicalNetworkFunction'
}
+// <<<<<<< Updated upstream
+// get configParameterMap() {
+// if (!this.model['config-parameter-map']) {
+// this.model['config-parameter-map'] = [];
+// }
+// return this.model['config-parameter-map'].map(d => DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(d, this)).map((fg, i) => {
+// return fg;
+// });
+// }
+
+// set configParameterMap(obj) {
+// const onVirtualNetworkFunctionAccessPointMap = (fg) => {
+
+// };
+// this.updateModelList('config-parameter-map', obj, VirtualNetworkFunctionAccessPointMap, onVirtualNetworkFunctionAccessPointMap);
+// }
+
+// createConfigParameterMap(model) {
+// model = model || DescriptorModelMetaFactory.createModelInstanceForType('nsd.config-parameter-map');
+// return this.configParameterMap = DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(model, this);
+// }
+// =======
+ get configParameterMap() {
+ if (!this.model['config-parameter-map']) {
+ this.model['config-parameter-map'] = [];
+ }
+ return this.model['config-parameter-map'].map(d => DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(d, this))
+ }
+
+ set configParameterMap(obj) {
+ this.updateModelList('config-parameter-map', obj, VirtualNetworkFunctionAccessPointMap);
+ }
+
+ createConfigParameterMap() {
+ const model = DescriptorModelMetaFactory.createModelInstanceForType('nsd.config-parameter-map');
+ return this.configParameterMap = DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(model, this);
+ }
+// >>>>>>> Stashed changes
+
get vnffgd() {
if (!this.model.vnffgd) {
this.model.vnffgd = [];
}
+
// NOTE temporarily disable NSD connection points
// https://trello.com/c/crVgg2A1/88-do-not-render-nsd-connection-in-the-composer
//get connectionPoint() {
--- /dev/null
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * 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.
+ *
+ */
+/**
+ * Created by onvelocity on 11/23/15.
+ */
+
+'use strict';
+
+import DescriptorModel from '../DescriptorModel'
+import DescriptorModelFactory from '../DescriptorModelFactory'
+
+export default class VnfapMap extends DescriptorModel {
+
+ static get type() {
+ return 'vnfap-map';
+ }
+
+ static get className() {
+ return 'VnfapMap';
+ }
+
+ constructor(model, parent) {
+ super(model, parent);
+ this.type = 'vnfap-map';
+ this.uiState['qualified-type'] = 'nsd.vnfap-map';
+ this.className = 'VnfapMap';
+ // this.addProp('vnfdRef', DescriptorModelFactory.newVirtualNetworkFunctionReadOnlyWrapper({}, this));
+ }
+
+ get id() {
+ return this.model.id;
+ }
+ get capability() {
+ return []
+ }
+
+ get dependency() {
+
+ }
+
+
+}
--- /dev/null
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * 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.
+ *
+ */
+/**
+ * Created by onvelocity on 11/23/15.
+ */
+
+'use strict';
+
+import DescriptorModel from '../DescriptorModel'
+import DescriptorModelFactory from '../DescriptorModelFactory'
+
+export default class ConfigParameterMap extends DescriptorModel {
+
+// <<<<<<< Updated upstream
+// static get type() {
+// return 'config-parameter-map';
+// }
+
+// static get className() {
+// return 'ConfigParameterMap';
+// }
+
+// constructor(model, parent) {
+// super(model, parent);
+// this.type = 'config-parameter-map';
+// this.uiState['qualified-type'] = 'nsd.config-parameter-map';
+// this.className = 'ConfigParameterMap';
+// // this.addProp('vnfdRef', DescriptorModelFactory.newVirtualNetworkFunctionReadOnlyWrapper({}, this));
+// }
+
+// get id() {
+// return this.model.id;
+// }
+// get capability() {
+// return {
+// 'member-vnf-index': this.model.capability['member-vnf-index'],
+// 'capability-ref': this.model.capability['capability-ref'],
+// }
+// }
+// get availableCapabilities() {
+
+// }
+
+// get dependency() {
+
+// }
+// =======
+ static get type() {
+ return 'config-parameter-map';
+ }
+
+ static get className() {
+ return 'ConfigParameterMap';
+ }
+ static get qualifiedType() {
+ return 'nsd.' + ConfigParameterMap.type;
+ }
+ constructor(model, parent) {
+ super(model, parent);
+ this.type = 'config-parameter-map';
+ this.uiState['qualified-type'] = 'nsd.config-parameter-map';
+ this.className = 'ConfigParameterMap';
+ // this.addProp('vnfdRef', DescriptorModelFactory.newVirtualNetworkFunctionReadOnlyWrapper({}, this));
+ }
+
+ get id() {
+ return this.model.id;
+ }
+ // get capability() {
+ // return {
+ // 'member-vnf-index': this.model.capability['member-vnf-index'],
+ // 'capability-ref': this.model.capability['capability-ref'],
+ // }
+ // }
+ // get availableCapabilities() {
+
+ // }
+
+ // get dependency() {
+
+ // }
+
+
+}
// Check if relative path or not
// TODO: Below works but
- // better to convert the pathCopy to absolute/rooted path
+ // better to convert the pathCopy to absolute/rooted path
// and use the absolute module instead
if (this.isRelativePath(leafRefPathCopy)) {
let i = pathArray.length;
- while (pathArray[pathArray.length - i] == '..') {
+ while ((pathArray[pathArray.length - i] == '..') && fieldKeyArray.length > 1) {
fieldKeyArray.splice(-1, 1);
if (!isNaN(Number(fieldKeyArray[fieldKeyArray.length - 1]))) {
// found a number, so an index. strip it
}
}
}
- } else {
+ } else {
// not supported - too many levels deep ... maybe some day
console.log('The relative path is from a node too many levels deep from root. This is not supported at the time');
}
this.setState({showJSONViewer: false});
}
- toggleCanvasPanelTray() {
+ toggleCanvasPanelTray(event) {
const layout = this.layout;
- if (layout.bottom > 25) {
+ const attrMap = event.target.attributes;
+ let panelEvent = null;
+ for(let k in attrMap) {
+ if(attrMap[k].name == 'data-event') {
+ panelEvent = attrMap[k].nodeValue;
+ }
+ }
+ if ((layout.bottom > 25) && ((panelEvent == this.displayedPanel) || panelEvent == 'arrow')) {
this.closeCanvasPanelTray();
} else {
this.openCanvasPanelTray();
}
+ this.setState({displayedPanel: panelEvent})
}
openCanvasPanelTray() {
min-width: 300px;
overflow: hidden;
z-index: 1;
+
.CanvasPanelHeader {
h1 {
margin: 0;
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
right: 0;
height: 25px;
min-width: 300px;
- background-color: white;
+ /* background-color: white;*/
+ background: $panel-bg-color;
&.-with-transitions {
transition: height 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000);
}
}
}
+
+ &-buttons {
+ display: -ms-flexbox;
+ display: flex;
+ margin-top: 2px;
+ button {
+ padding: 6px 34px;
+ }
+ }
+ .tray-body {
+ top:31px;
+ }
+ .ConfigParameterMap {
+
+ background: $panel-bg-color;
+
+ .EditDescriptorModelProperties {
+ margin-left: 8px;
+ }
+
+ .toggleable {
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ background: $panel-bg-color-contrast;
+ margin:8px 0;
+ & > .leaf-property {
+ -ms-flex: 1 0 100%;
+ flex: 1 0 100%;
+ margin: 8px 0;
+ padding: 0 8px;
+ background: none;
+ .property-label {
+ }
+ }
+ & > .container-property {
+ -ms-flex: 1 1;
+ flex: 1 1;
+ }
+ }
+ }
+
}
--- /dev/null
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * 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.
+ *
+ */
+
+@import "main";
+@import "ColorGroups";
+
+.config-parameter-map {
+
+ $field-border-radius: 3px;
+ $field-background-color: white;
+ $child-indent-left-right-margin: 5px;
+
+ font-size: smaller;
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+
+
+
+ width: 600px;
+
+ .config-parameter {
+ -ms-flex: 1 1 50%;
+ flex: 1 1 50%;
+ &-source, &-request {
+ border-top:1px solid $vld-primary-color;
+ }
+ }
+
+
+ h1 {
+ text-align: left;
+ span:last-child {
+ i {
+ display: none;
+ }
+ }
+ }
+
+ h2 {
+ @extend h1;
+
+ }
+
+ .basic-properties-group {
+ > h1 {
+ display: none;
+ }
+ }
+
+ .advanced-properties-group {
+ }
+
+ /* label is used as list item headers */
+ h3 {
+ @extend h2;
+ display: inline-block;
+ .name {
+ color: #586e75;
+ }
+ .value {
+ display: none;
+ color: #002b36;
+ }
+ .info {
+ margin: 4px;
+ }
+ }
+
+ val {
+ display: inline-block;
+ color: #073642;
+ }
+
+ a {
+ &.simple-list-item {
+ display: inline-block;
+ width: 230px;
+ padding: 4px 16px 4px 4px;
+ white-space: nowrap;
+ }
+ &.vld-list-item,
+ &.internal-vld-list-item{
+ border: 1px solid $vld-primary-color;
+ border-radius: 24px;
+ background: white linear-gradient(to right, $vld-primary-color 34px, white 34px);;
+ }
+ &.vnfd-list-item,
+ &.constituent-vnfd-list-item{
+ border: 1px solid $vnfd-primary-color;
+ border-radius: 11px;
+ background: white linear-gradient(to right, $vnfd-primary-color 34px, white 34px);;
+ }
+ &.vdu-list-item {
+ border: 1px solid $vdu-primary-color;
+ border-radius: 11px;
+ background: white linear-gradient(to right, $vdu-primary-color 34px, white 34px);;
+ }
+ &.vnffgd-list-item {
+ border: 1px solid $vnffgd-primary-color;
+ border-radius: 11px;
+ background: white linear-gradient(to right, $vnffgd-primary-color 34px, white 34px);;
+ }
+ }
+
+ &.-is-tree-view {
+ .property {
+
+ position: relative;
+ overflow: hidden;
+
+ margin: 8px 8px;
+
+ background-color: rgba(147, 161, 161, 0.5);
+ border-radius: $field-border-radius;
+
+ > h3 {
+ position: absolute;
+ top: 2px;
+ right: 18px;
+ height: 21px;
+ pointer-events: none;
+ border-radius: $field-border-radius;
+ padding: 3px 0;
+ }
+
+ > val {
+ width: 100%;
+ > .property-content {
+ width: 100%;
+ }
+
+ }
+
+ &.-is-focused {
+ > h3 {
+ /*z-index: -1;*/
+ }
+ }
+
+ &.leaf-property {
+ overflow: hidden;
+ min-height: 25px;
+ > h3 {
+ background: linear-gradient(to right, transparent, $field-background-color 21px);
+ padding-left: 25px;
+ }
+ > val {
+ border-radius: $field-border-radius;
+ > .property-content {
+ border-radius: $field-border-radius;
+ }
+ }
+ }
+
+ &.property:not(.leaf-property) {
+
+ padding: 7px $child-indent-left-right-margin 0 $child-indent-left-right-margin;
+
+ &.list-property {
+ > h3 {
+ padding: 4px 8px;
+ }
+ }
+
+ > h3 {
+ right: auto;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 25px;
+ z-index: 1;
+ padding: 8px;
+ /*background: red;*/
+ pointer-events: all;
+ }
+ > val {
+ margin: 28px 0 8px 0;
+ > .property-content {
+ position: relative;
+ border-radius: $field-border-radius;
+ margin-top: 4px;
+ padding: 4px;
+ &:first-of-type {
+ margin-top: 0;
+ }
+ &.simple-list {
+ min-height: 15px;
+ margin: 11px;
+ .simple-list-item {
+ img,
+ span {
+ margin: 0 5px;
+ vertical-align: middle;
+ }
+ }
+ }
+ }
+ > .property-content:not(.simple-list) {
+ &:nth-of-type(odd) {
+ background-color: rgba(238, 232, 213, 0.33);
+ }
+ &:nth-of-type(even) {
+ background-color: rgba(147, 161, 161, 0.33);
+ }
+ }
+ .tip {
+ font-style: italic;
+ font-size: small;
+ color: #93a1a1;
+ }
+ }
+ }
+
+ .actions {
+ span {
+ vertical-align: middle;
+ padding: 0 5px;
+ }
+ }
+
+ }
+
+ }
+
+ .description {
+ display: none;
+ }
+
+ input,
+ select,
+ textarea {
+ height: 25px;
+ line-height: 25px;
+ max-width: 100%;
+ min-width: 100%;
+ margin: 0;
+ padding: 0 0px 4px 8px;
+ border: 1px solid $field-background-color;
+ border-radius: $field-border-radius;
+ color: #002b36;
+ background-color: $field-background-color;
+ vertical-align: top;
+ &:focus {
+ color: #002b36;
+ background-color: white !important;
+ }
+ &::-webkit-input-placeholder {
+ color: #eee8d5 !important;
+ }
+
+ &:-moz-placeholder { /* Firefox 18- */
+ color: #eee8d5 !important;
+ }
+
+ &::-moz-placeholder { /* Firefox 19+ */
+ color: #eee8d5 !important;
+ }
+
+ &:-ms-input-placeholder {
+ color: #eee8d5 !important;
+ }
+ }
+
+ select {
+ padding-right: 0;
+ margin-right: 0;
+ -webkit-appearance: none;
+ -webkit-border-radius: $field-border-radius;
+ &.-value-not-set {
+ color: #eee8d5;
+ }
+ }
+
+ select {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none; /* using -prefix-free http://leaverou.github.io/prefixfree/*/
+ background: $field-background-color url(../../../node_modules/open-iconic/svg/caret-bottom.svg) no-repeat right 8px center;
+ background-size: 10px;
+ border: {
+ color: $field-background-color;
+ radius: $field-border-radius;
+ style: solid;
+ width: 1px;
+ }
+ }
+
+ /* Removes default arrow for IE10+*/
+ /* IE 8/9 get dafault arrow which covers caret image*/
+ /* as long as caret image is small than and positioned*/
+ /* behind default arrow*/
+ select::-ms-expand {
+ display: none;
+ }
+
+ textarea {
+ height: 50px;
+ }
+
+ input {
+ padding: 0 20px 0 8px;
+ line-height: 25px;
+ }
+
+ input[name$="id"],
+ input.-is-guid {
+ font-size: 10px;
+ font-family: monospace;
+ }
+
+}