Adding Prettier and fixing up eslint version
[sdc.git] / dox-sequence-diagram-ui / src / main / webapp / lib / ecomp / asdc / sequencer / components / editor / components / designer / components / actions / Actions.jsx
1 /*!
2  * Copyright © 2016-2017 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  */
16
17 import React from 'react';
18 import PropTypes from 'prop-types';
19 import Select from 'react-select';
20
21 import Common from '../../../../../../common/Common';
22 import Logger from '../../../../../../common/Logger';
23
24 import Icon from '../../../../../icons/Icon';
25 import iconSettings from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/settings.svg';
26 import iconExpanded from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/expanded.svg';
27 import iconCollapsed from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/collapsed.svg';
28 import iconOccurrenceDefault from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-default.svg';
29 import iconOccurrenceStart from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-start.svg';
30 import iconOccurrenceStop from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-stop.svg';
31 import iconFragmentDefault from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-default.svg';
32 import iconFragmentStart from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-start.svg';
33 import iconFragmentStop from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-stop.svg';
34
35 /**
36  * Action menu view.
37  */
38 class Actions extends React.Component {
39     // ///////////////////////////////////////////////////////////////////////////////////////////////
40
41     /**
42      * Construct view.
43      * @param props element properties.
44      * @param context react context.
45      */
46     constructor(props, context) {
47         super(props, context);
48
49         Logger.noop();
50
51         this.state = {
52             id: undefined,
53             visible: false
54         };
55
56         // Bindings.
57
58         this.show = this.show.bind(this);
59
60         this.onClickOccurrenceToggle = this.onClickOccurrenceToggle.bind(this);
61         this.onClickOccurrenceFrom = this.onClickOccurrenceFrom.bind(this);
62         this.onClickOccurrenceTo = this.onClickOccurrenceTo.bind(this);
63
64         this.onClickFragmentToggle = this.onClickFragmentToggle.bind(this);
65         this.onChangeFragmentGuard = this.onChangeFragmentGuard.bind(this);
66         this.onChangeFragmentOperator = this.onChangeFragmentOperator.bind(
67             this
68         );
69
70         this.onMouseOut = this.onMouseOut.bind(this);
71     }
72
73     // ///////////////////////////////////////////////////////////////////////////////////////////////
74
75     /**
76      * Show for message.
77      * @param id message ID.
78      * @param position xy coordinates.
79      */
80     show(id, position) {
81         const message = this.props.model.getMessageById(id);
82
83         let occurrencesToggle = false;
84         let fragmentToggle = false;
85         if (message) {
86             message.occurrences = message.occurrences || {
87                 start: [],
88                 stop: []
89             };
90             message.occurrences.start = message.occurrences.start || [];
91             message.occurrences.stop = message.occurrences.stop || [];
92             message.fragment = message.fragment || {};
93             message.fragment.start = message.fragment.start || false;
94             message.fragment.stop = message.fragment.stop || false;
95             message.fragment.guard = message.fragment.guard || '';
96             message.fragment.operator = message.fragment.operator || '';
97
98             const mo = message.occurrences;
99             occurrencesToggle = mo.start.length > 0 || mo.stop.length > 0;
100
101             const mf = message.fragment;
102             fragmentToggle = mf.start || mf.stop;
103         }
104
105         this.setState({
106             id,
107             message,
108             occurrencesToggle,
109             fragmentToggle,
110             visible: true,
111             x: position.x,
112             y: position.y
113         });
114     }
115
116     // ///////////////////////////////////////////////////////////////////////////////////////////////
117
118     /**
119      * Toggle occurrence state.
120      */
121     onClickOccurrenceToggle() {
122         const message = this.state.message;
123         if (message) {
124             const oFromState = Actions.getOccurrenceState(
125                 message.occurrences,
126                 message.from
127             );
128             const oToState = Actions.getOccurrenceState(
129                 message.occurrences,
130                 message.to
131             );
132             const oExpanded = oFromState > 0 || oToState > 0;
133             if (oExpanded) {
134                 this.setState({ occurrencesExpanded: true });
135             } else {
136                 const occurrencesExpanded = !this.state.occurrencesExpanded;
137                 this.setState({ occurrencesExpanded });
138             }
139         }
140     }
141
142     // ///////////////////////////////////////////////////////////////////////////////////////////////
143
144     /**
145      * Handle menu click.
146      */
147     onClickOccurrenceFrom() {
148         const message = this.state.message;
149         if (message) {
150             Actions._toggleOccurrence(message.occurrences, message.from);
151         }
152         this.setState({ message });
153         this.props.application.renderDiagram();
154     }
155
156     // ///////////////////////////////////////////////////////////////////////////////////////////////
157
158     /**
159      * Handle menu click.
160      */
161     onClickOccurrenceTo() {
162         const message = this.state.message;
163         if (message) {
164             Actions._toggleOccurrence(message.occurrences, message.to);
165         }
166         this.setState({ message });
167         this.props.application.renderDiagram();
168     }
169
170     // ///////////////////////////////////////////////////////////////////////////////////////////////
171
172     /**
173      * Toggle fragment.
174      */
175     onClickFragmentToggle() {
176         const message = this.state.message;
177         if (message) {
178             Actions._toggleFragment(message.fragment);
179         }
180         this.setState({ message });
181         this.props.application.renderDiagram();
182     }
183
184     // ///////////////////////////////////////////////////////////////////////////////////////////////
185
186     /**
187      * Handle menu click.
188      * @param event update event.
189      */
190     onChangeFragmentGuard(event) {
191         const message = this.state.message;
192         if (message) {
193             const options = this.props.application.getOptions();
194             message.fragment.guard = Common.sanitizeText(
195                 event.target.value,
196                 options,
197                 'guard'
198             );
199         }
200         this.setState({ message });
201         this.props.application.renderDiagram();
202     }
203
204     // ///////////////////////////////////////////////////////////////////////////////////////////////
205
206     /**
207      * Handle menu click.
208      * @param value updated value.
209      */
210     onChangeFragmentOperator(value) {
211         const message = this.state.message;
212         if (message) {
213             message.fragment.operator = value.value;
214         }
215         this.setState({ message });
216         this.props.application.renderDiagram();
217     }
218
219     // ///////////////////////////////////////////////////////////////////////////////////////////////
220
221     /**
222      * Handle mouse movement.
223      */
224     onMouseOut() {
225         this.setState({ id: -1, visible: false, x: 0, y: 0 });
226     }
227
228     // ///////////////////////////////////////////////////////////////////////////////////////////////
229
230     /**
231      * Render view.
232      * @returns {XML}
233      */
234     render() {
235         const actionsStyles = {};
236         const message = this.state.message;
237         if (!message || !this.state.visible) {
238             // Invisible.
239
240             return <div className="asdcs-actions" />;
241         }
242
243         // Position and display.
244
245         actionsStyles.display = 'block';
246         actionsStyles.left = this.state.x - 10;
247         actionsStyles.top = this.state.y - 10;
248
249         const oFromState = Actions.getOccurrenceState(
250             message.occurrences,
251             message.from
252         );
253         const oToState = Actions.getOccurrenceState(
254             message.occurrences,
255             message.to
256         );
257         const fState = Actions.getFragmentState(message.fragment);
258
259         const oExpanded =
260             this.state.occurrencesExpanded || oFromState > 0 || oToState > 0;
261         const oAuxClassName = oExpanded ? '' : 'asdcs-hidden';
262
263         const fExpanded = fState !== 0;
264         const fAuxClassName = fExpanded ? '' : 'asdcs-hidden';
265
266         const fragmentOperatorOptions = [
267             {
268                 value: 'alt',
269                 label: 'Alternate'
270             },
271             {
272                 value: 'opt',
273                 label: 'Optional'
274             },
275             {
276                 value: 'loop',
277                 label: 'Loop'
278             }
279         ];
280
281         const operator = message.fragment.operator || 'alt';
282
283         return (
284             <div
285                 className="asdcs-actions"
286                 style={actionsStyles}
287                 onMouseLeave={this.onMouseOut}>
288                 <div className="asdcs-actions-header">
289                     <div className="asdcs-actions-icon">
290                         <Icon glyph={iconSettings} />
291                     </div>
292                 </div>
293
294                 <div className="asdcs-actions-options">
295                     <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-occurrence">
296                         <div
297                             className="asdcs-actions-option asdcs-actions-option-occurrence-toggle"
298                             onClick={this.onClickOccurrenceToggle}>
299                             <span className="asdcs-label">Occurrence</span>
300                             <div className="asdcs-actions-state">
301                                 <Icon
302                                     glyph={iconCollapsed}
303                                     className={oExpanded ? 'asdcs-hidden' : ''}
304                                 />
305                                 <Icon
306                                     glyph={iconExpanded}
307                                     className={oExpanded ? '' : 'asdcs-hidden'}
308                                 />
309                             </div>
310                         </div>
311                     </div>
312
313                     <div
314                         className={`asdcs-actions-option asdcs-actions-option-occurrence-from ${oAuxClassName}`}
315                         onClick={this.onClickOccurrenceFrom}>
316                         <span className="asdcs-label">From</span>
317                         <div className="asdcs-actions-state">
318                             <span className="asdcs-annotation" />
319                             <Icon
320                                 glyph={iconOccurrenceDefault}
321                                 className={
322                                     oFromState === 0 ? '' : 'asdcs-hidden'
323                                 }
324                             />
325                             <Icon
326                                 glyph={iconOccurrenceStart}
327                                 className={
328                                     oFromState === 1 ? '' : 'asdcs-hidden'
329                                 }
330                             />
331                             <Icon
332                                 glyph={iconOccurrenceStop}
333                                 className={
334                                     oFromState === 2 ? '' : 'asdcs-hidden'
335                                 }
336                             />
337                         </div>
338                     </div>
339
340                     <div
341                         className={`asdcs-actions-option asdcs-actions-option-occurrence-to ${oAuxClassName}`}
342                         onClick={this.onClickOccurrenceTo}>
343                         <span className="asdcs-label">To</span>
344                         <div className="asdcs-actions-state">
345                             <span className="asdcs-annotation" />
346                             <Icon
347                                 glyph={iconOccurrenceDefault}
348                                 className={oToState === 0 ? '' : 'asdcs-hidden'}
349                             />
350                             <Icon
351                                 glyph={iconOccurrenceStart}
352                                 className={oToState === 1 ? '' : 'asdcs-hidden'}
353                             />
354                             <Icon
355                                 glyph={iconOccurrenceStop}
356                                 className={oToState === 2 ? '' : 'asdcs-hidden'}
357                             />
358                         </div>
359                     </div>
360
361                     <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-fragment">
362                         <div
363                             className="asdcs-actions-option asdcs-actions-fragment-toggle"
364                             onClick={this.onClickFragmentToggle}>
365                             <span className="asdcs-label">Fragment</span>
366                             <div className="asdcs-actions-state">
367                                 <span className="asdcs-annotation" />
368                                 <Icon
369                                     glyph={iconFragmentDefault}
370                                     className={
371                                         fState === 0 ? '' : 'asdcs-hidden'
372                                     }
373                                 />
374                                 <Icon
375                                     glyph={iconFragmentStart}
376                                     className={
377                                         fState === 1 ? '' : 'asdcs-hidden'
378                                     }
379                                 />
380                                 <Icon
381                                     glyph={iconFragmentStop}
382                                     className={
383                                         fState === 2 ? '' : 'asdcs-hidden'
384                                     }
385                                 />
386                             </div>
387                         </div>
388                     </div>
389
390                     <div
391                         className={`asdcs-actions-option asdcs-actions-fragment-operator ${fAuxClassName}`}>
392                         <div className="asdcs-label">Operator</div>
393                         <div className="asdcs-value">
394                             <Select
395                                 className="asdcs-editable-select"
396                                 openOnFocus
397                                 clearable={false}
398                                 searchable={false}
399                                 value={operator}
400                                 onChange={this.onChangeFragmentOperator}
401                                 options={fragmentOperatorOptions}
402                             />
403                         </div>
404                     </div>
405
406                     <div
407                         className={`asdcs-actions-option asdcs-actions-fragment-guard ${fAuxClassName}`}>
408                         <div className="asdcs-label">Guard</div>
409                         <div className="asdcs-value">
410                             <input
411                                 className="asdcs-editable"
412                                 type="text"
413                                 size="20"
414                                 maxLength="80"
415                                 value={message.fragment.guard}
416                                 placeholder="Condition"
417                                 onChange={this.onChangeFragmentGuard}
418                             />
419                         </div>
420                     </div>
421                 </div>
422
423                 <div className="asdcs-actions-footer" />
424             </div>
425         );
426     }
427
428     // ///////////////////////////////////////////////////////////////////////////////////////////////
429
430     /**
431      * Toggle through three occurrence states on click.
432      * @param occurrence occurrences state, updated as side-effect.
433      * @param lifelineId message end that's being toggled.
434      * @private
435      */
436     static _toggleOccurrence(occurrence, lifelineId) {
437         const o = occurrence;
438
439         const rm = function rm(array, value) {
440             const index = array.indexOf(value);
441             if (index !== -1) {
442                 array.splice(index, 1);
443             }
444         };
445
446         const add = function add(array, value) {
447             if (array.indexOf(value) === -1) {
448                 array.push(value);
449             }
450         };
451
452         if (o.start && o.start.indexOf(lifelineId) !== -1) {
453             // Start -> stop.
454             rm(o.start, lifelineId);
455             add(o.stop, lifelineId);
456         } else if (o.stop && o.stop.indexOf(lifelineId) !== -1) {
457             // Stop -> default.
458             rm(o.start, lifelineId);
459             rm(o.stop, lifelineId);
460         } else {
461             // Default -> start.
462             add(o.start, lifelineId);
463             rm(o.stop, lifelineId);
464         }
465     }
466
467     // ///////////////////////////////////////////////////////////////////////////////////////////////
468
469     /**
470      * Toggle fragment setting on click.
471      * @param fragment
472      * @private
473      **/
474     static _toggleFragment(fragment) {
475         const f = fragment;
476         if (f.start === true) {
477             f.start = false;
478             f.stop = true;
479         } else if (f.stop === true) {
480             f.stop = false;
481             f.start = false;
482         } else {
483             f.start = true;
484             f.stop = false;
485         }
486         f.guard = '';
487         f.operator = '';
488     }
489
490     // ///////////////////////////////////////////////////////////////////////////////////////////////
491
492     /**
493      * Get ternary occurrences state.
494      * @param o occurrences.
495      * @param lifelineId from/to lifeline ID.
496      * @returns {number}
497      * @private
498      */
499     static getOccurrenceState(o, lifelineId) {
500         if (o.start.indexOf(lifelineId) !== -1) {
501             return 1;
502         }
503         if (o.stop.indexOf(lifelineId) !== -1) {
504             return 2;
505         }
506         return 0;
507     }
508
509     // ///////////////////////////////////////////////////////////////////////////////////////////////
510
511     /**
512      * Get ternary fragment state.
513      * @param f fragment.
514      * @returns {number}
515      * @private
516      */
517     static getFragmentState(f) {
518         if (f.start) {
519             return 1;
520         }
521         if (f.stop) {
522             return 2;
523         }
524         return 0;
525     }
526 }
527
528 /** Element properties. */
529 Actions.propTypes = {
530     application: PropTypes.object.isRequired,
531     model: PropTypes.object.isRequired
532 };
533
534 export default Actions;