535a928b41bbb111c45daf9a7884d30a460982fb
[osm/UI.git] / skyquake / plugins / composer / src / src / components / CatalogPanel.js
1
2 /*
3 *
4 * Copyright 2016 RIFT.IO Inc
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 'use strict';
20
21 import _ from 'lodash'
22 import React from 'react'
23 import ReactDOM from 'react-dom'
24 import messages from './messages'
25 import ClassNames from 'classnames'
26 import UploadDropZone from '../libraries/CatalogPackageManagerUploadDropZone'
27 import PureRenderMixin from 'react-addons-pure-render-mixin'
28 import DropTarget from './DropTarget'
29 import DropZonePanel from './DropZonePanel'
30 import CatalogItems from './CatalogItems'
31 import CatalogFilter from './CatalogFilter'
32 import CatalogPanelTray from './CatalogPanelTray'
33 import CatalogPanelToolbar from './CatalogPanelToolbar'
34 import CatalogPackageManager from './CatalogPackageManager'
35 import CatalogItemsActions from '../actions/CatalogItemsActions'
36 import CatalogPanelTrayActions from '../actions/CatalogPanelTrayActions'
37 import ComposerAppActions from '../actions/ComposerAppActions'
38 import ComposerAppStore from '../stores/ComposerAppStore'
39 import CatalogPanelStore from '../stores/CatalogPanelStore'
40 import LoadingIndicator from './LoadingIndicator'
41 import SelectionManager from '../libraries/SelectionManager'
42
43 import '../styles/CatalogPanel.scss'
44
45 const createDropZone = function (action, clickable, dropTarget) {
46 const dropZone = new UploadDropZone(ReactDOM.findDOMNode(dropTarget), [clickable], action);
47 dropZone.on('dragover', this.onDragOver);
48 dropZone.on('dragend', this.onDragEnd);
49 dropZone.on('addedfile', this.onFileAdded);
50 return dropZone;
51 };
52
53 const uiTransientState = {
54 isDrop: false,
55 isDragging: false,
56 isDraggingFiles: false,
57 timeoutId: 0
58 };
59
60 const CatalogPanel = React.createClass({
61 mixins: [PureRenderMixin],
62 getInitialState() {
63 return CatalogPanelStore.getState();
64 },
65 getDefaultProps() {
66 },
67 componentWillMount() {
68 },
69 componentDidMount() {
70 CatalogPanelStore.listen(this.onChange);
71 document.body.addEventListener('dragover', this.onDragOver);
72 document.body.addEventListener('dragend', this.onDragEnd);
73 window.addEventListener('dragend', this.onDragEnd);
74 },
75 componentDidUpdate() {
76 },
77 componentWillUnmount() {
78 CatalogPanelStore.unlisten(this.onChange);
79 document.body.removeEventListener('dragover', this.onDragOver);
80 document.body.removeEventListener('dragend', this.onDragEnd);
81 window.removeEventListener('dragend', this.onDragEnd);
82 },
83 render() {
84
85 const onDropCatalogItem = e => {
86 e.preventDefault();
87 clearTimeout(uiTransientState.timeoutId);
88 uiTransientState.isDragging = false;
89 uiTransientState.isDrop = true;
90 const item = JSON.parse(e.dataTransfer.getData('text')).item;
91 CatalogItemsActions.exportSelectedCatalogItems(item);
92 CatalogPanelTrayActions.open();
93 };
94
95 const onDropUpdatePackage = e => {
96 e.preventDefault();
97 clearTimeout(uiTransientState.timeoutId);
98 uiTransientState.isDragging = false;
99 uiTransientState.isDrop = true;
100 CatalogPanelTrayActions.open();
101 };
102
103 const onDropOnboardPackage = e => {
104 e.preventDefault();
105 clearTimeout(uiTransientState.timeoutId);
106 uiTransientState.isDragging = false;
107 uiTransientState.isDrop = true;
108 CatalogPanelTrayActions.open();
109 };
110
111 const isDraggingItem = uiTransientState.isDragging && !uiTransientState.isDraggingFiles;
112 const isDraggingFiles = uiTransientState.isDragging && uiTransientState.isDraggingFiles;
113 const updateDropZone = createDropZone.bind(this, UploadDropZone.ACTIONS.update, '.action-update-catalog-package');
114 const onboardDropZone = createDropZone.bind(this, UploadDropZone.ACTIONS.onboard, '.action-onboard-catalog-package');
115 const className = ClassNames('CatalogPanel', {'-is-tray-open': this.state.isTrayOpen});
116 const hasNoCatalogs = this.props.hasNoCatalogs;
117 const isLoading = this.props.isLoading;
118 return (
119 <div className={className} data-resizable="right" data-resizable-handle-offset="0 6" style={{width: this.props.layout.left}}>
120 <CatalogPanelToolbar />
121 <div className="CatalogPanelBody">
122 {(() => {
123 if (isLoading) {
124 return (
125 <div className="LoaderWrapper">
126 <LoadingIndicator />
127 </div>
128 )
129 }
130 if (hasNoCatalogs) {
131 return messages.catalogWelcome;
132 }
133 return (
134 <div>
135 <CatalogFilter filterByType={this.props.filterByType} />
136 <CatalogItems filterByType={this.props.filterByType} />
137 </div>
138 );
139 })()}
140 </div>
141 <CatalogPanelTray show={this.state.isTrayOpen}>
142 <DropZonePanel show={isDraggingItem} title="Drop catalog item to export.">
143 <DropTarget className="action-export-catalog-items" onDrop={onDropCatalogItem}>
144 <span>Export catalog item.</span>
145 </DropTarget>
146 </DropZonePanel>
147 <DropZonePanel show={isDraggingFiles}>
148 <DropTarget className="action-onboard-catalog-package" dropZone={onboardDropZone} onDrop={onDropOnboardPackage}>
149 <span>On-board new catalog package.</span>
150 </DropTarget>
151 <DropTarget className="action-update-catalog-package" dropZone={updateDropZone} onDrop={onDropUpdatePackage}>
152 <span>Update existing catalog package.</span>
153 </DropTarget>
154 </DropZonePanel>
155 <CatalogPackageManager />
156 </CatalogPanelTray>
157 </div>
158 );
159
160 },
161 onChange(state) {
162 this.setState(state);
163 },
164 onDragOver(e) {
165 // NOTE do not call preventDefault here - see DropTarget
166 if (!uiTransientState.isDragging) {
167 uiTransientState.isDrop = false;
168 uiTransientState.isDragging = true;
169 uiTransientState.wasTrayOpen = this.state.isTrayOpen;
170 uiTransientState.isDraggingFiles = _.contains(e.dataTransfer.types, 'Files');
171 const dragState = ComposerAppStore.getState().drag || {};
172 if (uiTransientState.isDraggingFiles || (dragState.type === 'catalog-item')) {
173 CatalogPanelTrayActions.open();
174 }
175 }
176 e.dataTransfer.dropEffect = 'none';
177 // the drag-end event does not fire on drag events that originate
178 // outside of the browser, e.g. dragging files from desktop, so
179 // we use this debounced callback to simulate a drag-end event
180 clearTimeout(uiTransientState.timeoutId);
181 uiTransientState.timeoutId = setTimeout(() => {
182 this.onDragEnd();
183 }, 400);
184 },
185 onDragEnd() {
186 clearTimeout(uiTransientState.timeoutId);
187 if (uiTransientState.isDragging) {
188 uiTransientState.isDragging = false;
189 if (uiTransientState.isDrop || uiTransientState.wasTrayOpen) {
190 CatalogPanelTrayActions.open();
191 } else {
192 CatalogPanelTrayActions.close();
193 }
194 }
195 },
196 onFileAdded() {
197 CatalogPanelTrayActions.open();
198 }
199 });
200
201 export default CatalogPanel;