3 * Copyright 2016 RIFT.IO Inc
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 window
['RIFT_wareLaunchpadComposerVersion'] = `semver 0.0.79`;
23 import 'babel-polyfill'
24 import alt
from '../alt'
25 import UID
from '../libraries/UniqueId'
26 import utils
from '../libraries/utils'
27 import React
from 'react'
28 import ReactDOM
from 'react-dom'
29 import Crouton
from 'react-crouton'
30 import ClassNames
from 'classnames'
31 import PureRenderMixin
from 'react-addons-pure-render-mixin'
32 import DeletionManager
from '../libraries/DeletionManager'
33 import SelectionManager
from '../libraries/SelectionManager'
34 import ResizableManager
from '../libraries/ResizableManager'
35 import DescriptorModelMetaFactory
from '../libraries/model/DescriptorModelMetaFactory'
36 import DescriptorModelFactory
from '../libraries/model/DescriptorModelFactory'
37 import RiftHeader
from './RiftHeader'
38 import CanvasPanel
from './CanvasPanel'
39 import CatalogPanel
from './CatalogPanel'
40 import DetailsPanel
from './DetailsPanel'
41 import ModalOverlay
from './ModalOverlay'
42 import ComposerAppToolbar
from './ComposerAppToolbar'
43 import PanelResizeAction
from '../actions/PanelResizeAction'
44 import ComposerAppActions
from '../actions/ComposerAppActions'
45 import ComposerAppStore
from '../stores/ComposerAppStore'
46 import CatalogDataStore
from '../stores/CatalogDataStore'
47 import TooltipManager
from '../libraries/TooltipManager'
48 import CatalogItemsActions
from '../actions/CatalogItemsActions'
49 import CommonUtils
from 'utils/utils.js'
50 import FileManagerActions
from './filemanager/FileManagerActions';
51 import { SkyquakeRBAC
, isRBACValid
} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
52 import ROLES
from 'utils/roleConstants.js';
53 import _isEmpty
from 'lodash/isEmpty';
54 import _isEqual
from 'lodash/isEqual';
56 import 'normalize.css'
57 import '../styles/AppRoot.scss'
58 import 'style/layout.scss'
61 const resizeManager
= new ResizableManager(window
);
63 const clearLocalStorage
= utils
.getSearchParams(window
.location
).hasOwnProperty('clearLocalStorage');
65 const preventDefault
= e
=> e
.preventDefault();
66 const clearDragState
= () => ComposerAppActions
.setDragState(null);
68 const PROJECT_ROLES
= ROLES
.PROJECT
;
69 const PLATFORM
= ROLES
.PLATFORM
;
71 const CATALOG_POLLING_INTERVAL
= 2000;
74 const ComposerApp
= React
.createClass({
76 return ComposerAppStore
.getState();
82 router
: React
.PropTypes
.object
,
83 userProfile
: React
.PropTypes
.object
85 componentWillUpdate(nextProps
, nextState
, nextContext
) {
86 if (!_isEmpty(nextContext
.userProfile
)) {
87 CatalogDataStore
.setUserProfile(nextContext
.userProfile
);
90 shouldComponentUpdate: function (nextProps
, nextState
, nextContext
) {
91 if (!this.userProfile
&& !_isEmpty(nextContext
.userProfile
)) {
92 this.userProfile
= nextContext
.userProfile
;
93 CatalogDataStore
.setUserProfile(nextContext
.userProfile
);
96 return !_isEqual(this.props
, nextProps
) ||
97 !_isEqual(this.state
, nextState
);
99 componentWillMount() {
100 if (clearLocalStorage
) {
101 window
.localStorage
.clear();
104 FileManagerActions
.openFileManagerSockets();
106 this.state
.isLoading
= CatalogDataStore
.getState().isLoading
;
107 ComposerAppStore
.listen(this.onChange
);
108 CatalogDataStore
.listen(this.onCatalogDataChanged
);
109 window
.addEventListener('resize', this.resize
);
110 // prevent browser from downloading any drop outside of our specific drop zones
111 window
.addEventListener('dragover', preventDefault
);
112 window
.addEventListener('drop', preventDefault
);
113 // ensure drags initiated in the app clear the state on drop
114 window
.addEventListener('drop', clearDragState
);
115 DeletionManager
.addEventListeners();
117 componentWillUnmount() {
118 window
.removeEventListener('resize', this.resize
);
119 window
.removeEventListener('dragover', preventDefault
);
120 window
.removeEventListener('drop', preventDefault
);
121 window
.removeEventListener('drop', clearDragState
);
122 FileManagerActions
.closeFileManagerSockets();
123 // resizeManager automatically registered its event handlers
124 resizeManager
.removeAllEventListeners();
125 ComposerAppStore
.unlisten(this.onChange
);
126 CatalogDataStore
.unlisten(this.onCatalogDataChanged
);
127 DeletionManager
.removeEventListeners();
128 TooltipManager
.removeEventListeners();
129 if (this.catalogMonitorId
) {
130 clearTimeout(this.catalogMonitorId
);
133 componentDidMount() {
134 resizeManager
.addAllEventListeners();
135 const snapshot
= window
.localStorage
.getItem('composer');
137 alt
.bootstrap(snapshot
);
139 document
.body
.addEventListener('keydown', (event
) => {
140 // prevent details editor form from blowing up the app
141 const ENTER_KEY
= 13;
142 if (event
.which
=== ENTER_KEY
) {
143 event
.preventDefault();
147 const loadCatalogs
= () => {
148 CatalogDataStore
.loadCatalogs();
149 if (CATALOG_POLLING_INTERVAL
) {
150 this.catalogMonitorId
= setTimeout(loadCatalogs
, CATALOG_POLLING_INTERVAL
);
154 DescriptorModelMetaFactory
.init().then(() => this.setState({ hasModel
: true }));
156 componentDidUpdate() {
157 if (this.state
.fullScreenMode
) {
158 document
.body
.classList
.add('-is-full-screen');
160 document
.body
.classList
.remove('-is-full-screen');
162 SelectionManager
.refreshOutline();
165 PanelResizeAction
.resize(e
);
170 const User
= this.userProfile
|| {};
171 const rbacDisabled
= !isRBACValid(User
, [PROJECT_ROLES
.PROJECT_ADMIN
, PROJECT_ROLES
.CATALOG_ADMIN
]);
172 if (this.state
.hasModel
) {
174 function onClickUpdateSelection(event
) {
175 if (event
.defaultPrevented
) {
178 const element
= SelectionManager
.getClosestElementWithUID(event
.target
);
180 SelectionManager
.select(element
);
181 SelectionManager
.refreshOutline();
182 event
.preventDefault();
184 if (event
.target
.offsetParent
&& !event
.target
.offsetParent
.classList
.contains("tray-body")) {
185 SelectionManager
.clearSelectionAndRemoveOutline();
191 let AppHeader
= (<div className
="AppHeader">
195 const classNames
= ClassNames('ComposerApp');
196 const isNew
= self
.state
.item
&& self
.state
.item
.uiState
.isNew
;
197 const hasItem
= self
.state
.item
&& self
.state
.item
.uiState
;
198 const isModified
= self
.state
.item
&& self
.state
.item
.uiState
.modified
;
199 const isEditingNSD
= self
.state
.item
&& self
.state
.item
.uiState
&& /nsd/.test(self
.state
.item
.uiState
.type
);
200 const isEditingVNFD
= self
.state
.item
&& self
.state
.item
.uiState
&& /vnfd/.test(self
.state
.item
.uiState
.type
);
201 const containers
= [self
.state
.item
].reduce(DescriptorModelFactory
.buildCatalogItemFactory(CatalogDataStore
.getState().catalogs
), []);
203 containers
.filter(d
=> DescriptorModelFactory
.isConnectionPoint(d
)).forEach(d
=> {
204 d
.cpNumber
= ++cpNumber
;
205 containers
.filter(d
=> DescriptorModelFactory
.isVnfdConnectionPointRef(d
)).filter(ref
=> ref
.key
=== d
.key
).forEach(ref
=> ref
.cpNumber
= d
.cpNumber
);
207 const canvasTitle
= containers
.length
? containers
[0].model
.name
: '';
208 const hasNoCatalogs
= CatalogDataStore
.getState().catalogs
.length
=== 0;
209 const isLoading
= self
.state
.isLoading
;
211 //Bridge element for Crouton fix. Should eventually put Composer on same flux context
212 const Bridge
= this.state
.ComponentBridgeElement
;
215 <div ref
={element
=> TooltipManager
.addEventListeners(element
)} id
="RIFT_wareLaunchpadComposerAppRoot" className
="AppRoot" onClick
={onClickUpdateSelection
}>
217 <i className
="corner-accent top left" />
218 <i className
="corner-accent top right" />
219 <i className
="corner-accent bottom left" />
220 <i className
="corner-accent bottom right" />
222 <div className
="AppBody">
223 <div className
={classNames
}>
224 <CatalogPanel layout
={self
.state
.layout
}
225 isLoading
={isLoading
}
226 hasNoCatalogs
={hasNoCatalogs
}
227 filterByType
={self
.state
.filterCatalogByTypeValue
}
228 rbacDisabled
={rbacDisabled
} />
229 <CanvasPanel layout
={self
.state
.layout
}
230 hasNoCatalogs
={hasNoCatalogs
}
231 showMore
={self
.state
.showMore
}
232 containers
={containers
}
234 zoom
={self
.state
.zoom
}
235 panelTabShown
={self
.state
.panelTabShown
}
236 files
={self
.state
.files
}
237 filesState
={self
.state
.filesState
}
238 newPathName
={self
.state
.newPathName
}
239 item
={self
.state
.item
}
240 type
={self
.state
.filterCatalogByTypeValue
}
241 rbacDisabled
={rbacDisabled
}
245 (self
.state
.panelTabShown
== 'descriptor') ?
246 <DetailsPanel layout
={self
.state
.layout
}
247 hasNoCatalogs
={hasNoCatalogs
}
248 showMore
={self
.state
.showMore
}
249 containers
={containers
}
250 showHelp
={self
.state
.showHelp
}
251 collapsePanelsByDefault
={self
.state
.collapsePanelsByDefault
}
252 openPanelsWithData
={self
.state
.openPanelsWithData
}
253 showJSONViewer
={self
.state
.showJSONViewer
} />
257 <ComposerAppToolbar layout
={self
.state
.layout
}
258 showMore
={self
.state
.showMore
}
259 isEditingNSD
={isEditingNSD
}
260 isEditingVNFD
={isEditingVNFD
}
261 isModified
={isModified
}
263 disabled
={!hasItem
|| rbacDisabled
}
264 onClick
={event
=> event
.stopPropagation()}
265 panelTabShown
={self
.state
.panelTabShown
} />
275 this.setState(state
);
277 onCatalogDataChanged(catalogDataState
) {
279 isLoading
: catalogDataState
.isLoading
285 export default ComposerApp
;