goblin-gadgets 4.0.17 → 4.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.js +5 -2
- package/package.json +3 -3
- package/popup-dispatcher.js +13 -0
- package/widgets/label-text-field/widget.js +0 -1
- package/widgets/popup-container/styles.js +248 -0
- package/widgets/popup-container/widget.js +69 -0
- package/widgets/popup-dispatcher/service.js +108 -0
- package/widgets/popup-dispatcher/widget.js +112 -0
- package/widgets/tab-navigation/service.js +4 -3
package/eslint.config.js
CHANGED
|
@@ -15,7 +15,10 @@ module.exports = [
|
|
|
15
15
|
languageOptions: {
|
|
16
16
|
parser: babelParser,
|
|
17
17
|
parserOptions: {
|
|
18
|
-
requireConfigFile: false,
|
|
18
|
+
requireConfigFile: false, // Évite de devoir spécifier un fichier de configuration Babel
|
|
19
|
+
babelOptions: {
|
|
20
|
+
presets: ['@babel/preset-react'],
|
|
21
|
+
},
|
|
19
22
|
ecmaFeatures: {
|
|
20
23
|
jsx: true,
|
|
21
24
|
},
|
|
@@ -36,7 +39,7 @@ module.exports = [
|
|
|
36
39
|
'eqeqeq': 'error',
|
|
37
40
|
'no-console': 'off',
|
|
38
41
|
'react/display-name': 'off',
|
|
39
|
-
'@babel/no-unused-expressions': 'error',
|
|
42
|
+
'@babel/no-unused-expressions': 'error', // Utilisation de règles spécifiques à Babel
|
|
40
43
|
'no-unused-vars': [
|
|
41
44
|
'error',
|
|
42
45
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goblin-gadgets",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.23",
|
|
4
4
|
"description": "Gadgets library",
|
|
5
5
|
"main": "./builders/builders.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"goblin-theme": "^2.0.0",
|
|
30
30
|
"goblin-laboratory": "^4.0.0",
|
|
31
31
|
"leaflet": "1.2.0",
|
|
32
|
-
"mocha": "^
|
|
32
|
+
"mocha": "^10.7.3",
|
|
33
33
|
"monaco-editor": "^0.31.1",
|
|
34
34
|
"mousetrap": "^1.6.1",
|
|
35
35
|
"prettier": "2.0.4",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"scroll-into-view-if-needed": "^1.4.0",
|
|
45
45
|
"xcraft-core-shredder": "^5.0.0",
|
|
46
46
|
"xcraft-dev-prettier": "^2.0.0",
|
|
47
|
-
"xcraft-dev-rules": "^4.4.
|
|
47
|
+
"xcraft-dev-rules": "^4.4.2"
|
|
48
48
|
},
|
|
49
49
|
"prettier": "xcraft-dev-prettier"
|
|
50
50
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve the list of available commands.
|
|
5
|
+
*
|
|
6
|
+
* @returns {Object} The list and definitions of commands.
|
|
7
|
+
*/
|
|
8
|
+
exports.xcraftCommands = function () {
|
|
9
|
+
return require(`./widgets/${require('path').basename(
|
|
10
|
+
__filename,
|
|
11
|
+
'.js'
|
|
12
|
+
)}/service.js`);
|
|
13
|
+
};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {Unit} from 'goblin-theme';
|
|
2
|
+
const px = Unit.toPx;
|
|
3
|
+
const n = Unit.toValue;
|
|
4
|
+
|
|
5
|
+
/******************************************************************************/
|
|
6
|
+
|
|
7
|
+
export const propNames = [
|
|
8
|
+
'visibility',
|
|
9
|
+
'size',
|
|
10
|
+
'origin',
|
|
11
|
+
'attach',
|
|
12
|
+
'width',
|
|
13
|
+
'height',
|
|
14
|
+
'heightStrategy',
|
|
15
|
+
'marginTopBottom',
|
|
16
|
+
'attachPoint',
|
|
17
|
+
'transitionScope',
|
|
18
|
+
'triangle',
|
|
19
|
+
'triangleSize',
|
|
20
|
+
'screenBackground',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export default function styles(theme, props) {
|
|
24
|
+
const {
|
|
25
|
+
visibility,
|
|
26
|
+
size,
|
|
27
|
+
origin,
|
|
28
|
+
attach,
|
|
29
|
+
width,
|
|
30
|
+
height,
|
|
31
|
+
heightStrategy,
|
|
32
|
+
marginTopBottom,
|
|
33
|
+
attachPoint,
|
|
34
|
+
transitionScope = 'all',
|
|
35
|
+
triangle,
|
|
36
|
+
triangleSize = '16px',
|
|
37
|
+
screenBackground = 'rgba(255,255,255,0.8)',
|
|
38
|
+
} = props;
|
|
39
|
+
|
|
40
|
+
const showed = visibility === 'show';
|
|
41
|
+
const full = size === 'fullscreen';
|
|
42
|
+
const fitToContent = heightStrategy === 'fit-to-content';
|
|
43
|
+
|
|
44
|
+
let left = '0px';
|
|
45
|
+
let right = '0px';
|
|
46
|
+
let top = '0px';
|
|
47
|
+
let bottom = fitToContent ? null : '0px';
|
|
48
|
+
|
|
49
|
+
let marginLeft = 'calc(50% - 600px + 50px)';
|
|
50
|
+
let marginRight = 'calc(50% - 600px + 50px)';
|
|
51
|
+
let marginTop = '100px';
|
|
52
|
+
let marginBottom = '100px';
|
|
53
|
+
let maxHeight = fitToContent ? 'calc(100% - 200px)' : null;
|
|
54
|
+
|
|
55
|
+
if (width) {
|
|
56
|
+
const w = n(width);
|
|
57
|
+
marginLeft = `calc(50% - ${px(w / 2)})`;
|
|
58
|
+
marginRight = `calc(50% - ${px(w / 2)})`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (width && attach && attach.includes('left')) {
|
|
62
|
+
left = 'calc(50% - 600px + 100px)';
|
|
63
|
+
right = null;
|
|
64
|
+
marginLeft = null;
|
|
65
|
+
marginRight = null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (width && attach && attach.includes('right')) {
|
|
69
|
+
left = null;
|
|
70
|
+
right = 'calc(50% - 600px + 100px)';
|
|
71
|
+
marginLeft = null;
|
|
72
|
+
marginRight = null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (height && attach && attach.includes('top')) {
|
|
76
|
+
bottom = null;
|
|
77
|
+
marginTop = '100px';
|
|
78
|
+
marginBottom = null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (height && attach && attach.includes('bottom')) {
|
|
82
|
+
top = null;
|
|
83
|
+
marginBottom = '100px';
|
|
84
|
+
marginTop = null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (height && !attach) {
|
|
88
|
+
const h = n(height);
|
|
89
|
+
top = '50%';
|
|
90
|
+
bottom = '50%';
|
|
91
|
+
marginTop = px(-h / 2);
|
|
92
|
+
marginBottom = null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (marginTopBottom) {
|
|
96
|
+
marginTop = marginTopBottom;
|
|
97
|
+
marginBottom = marginTopBottom;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (attachPoint) {
|
|
101
|
+
// Compute the attach point without overflow managment.
|
|
102
|
+
const al = px(attachPoint.x - n(width) / 2);
|
|
103
|
+
// Manages the overflow on the right.
|
|
104
|
+
const x = `min(${al}, 100vw - ${width} - 10px)`;
|
|
105
|
+
// Manages the overflow on the left.
|
|
106
|
+
const ml = `max(${x}, 10px)`;
|
|
107
|
+
|
|
108
|
+
// Cases where the popup goes out of the window are not handled.
|
|
109
|
+
switch (triangle) {
|
|
110
|
+
case 'bottom':
|
|
111
|
+
marginLeft = ml;
|
|
112
|
+
marginRight = null;
|
|
113
|
+
marginTop = px(attachPoint.y - n(height) - n(triangleSize));
|
|
114
|
+
marginBottom = null;
|
|
115
|
+
break;
|
|
116
|
+
case 'top':
|
|
117
|
+
marginLeft = ml;
|
|
118
|
+
marginRight = null;
|
|
119
|
+
marginTop = px(attachPoint.y + n(triangleSize));
|
|
120
|
+
marginBottom = null;
|
|
121
|
+
break;
|
|
122
|
+
case 'left':
|
|
123
|
+
marginLeft = px(attachPoint.x + n(triangleSize));
|
|
124
|
+
marginRight = null;
|
|
125
|
+
marginTop = px(attachPoint.y - n(height) / 2);
|
|
126
|
+
marginBottom = null;
|
|
127
|
+
break;
|
|
128
|
+
case 'right':
|
|
129
|
+
marginLeft = px(attachPoint.x - n(width) - n(triangleSize));
|
|
130
|
+
marginRight = null;
|
|
131
|
+
marginTop = px(attachPoint.y - n(height) / 2);
|
|
132
|
+
marginBottom = null;
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
console.error(
|
|
136
|
+
`popupContainer: triangle position '${triangle}' not supported`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (full) {
|
|
142
|
+
marginLeft = null;
|
|
143
|
+
marginRight = null;
|
|
144
|
+
marginTop = null;
|
|
145
|
+
marginBottom = null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const popupContainer = {
|
|
149
|
+
position: 'absolute',
|
|
150
|
+
inset: '0',
|
|
151
|
+
overflow: 'hidden',
|
|
152
|
+
backgroundColor: showed ? screenBackground : null,
|
|
153
|
+
transition: theme.transitions.openClosePopup,
|
|
154
|
+
opacity: showed ? 1 : 0,
|
|
155
|
+
pointerEvents: showed ? 'auto' : 'none',
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const window = {
|
|
159
|
+
position: 'absolute',
|
|
160
|
+
left,
|
|
161
|
+
right,
|
|
162
|
+
top,
|
|
163
|
+
bottom,
|
|
164
|
+
marginLeft,
|
|
165
|
+
marginRight,
|
|
166
|
+
marginTop,
|
|
167
|
+
marginBottom,
|
|
168
|
+
width,
|
|
169
|
+
height,
|
|
170
|
+
maxHeight,
|
|
171
|
+
backgroundColor: '#fff',
|
|
172
|
+
borderRadius: full ? null : theme.shapes.popupRadius,
|
|
173
|
+
boxShadow: full ? null : '0px 0px 40px 0px rgba(0,0,0,0.5)',
|
|
174
|
+
display: 'flex',
|
|
175
|
+
flexDirection: 'column',
|
|
176
|
+
overflow: 'hidden',
|
|
177
|
+
transformOrigin: origin || 'bottom',
|
|
178
|
+
transition: `${transitionScope} ${theme.transitions.openClosePopup}`,
|
|
179
|
+
transform: showed ? null : 'scale(0.5)', // (*)
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// (*) Ne pas faire:
|
|
183
|
+
// transform: showed ? 'scale(1)' : 'scale(0.5)',
|
|
184
|
+
// Cela génère un bug étrange lors du drag dans ColorPicker et AnalogClock !
|
|
185
|
+
|
|
186
|
+
/******************************************************************************/
|
|
187
|
+
|
|
188
|
+
const windowTriangle = {
|
|
189
|
+
...window,
|
|
190
|
+
backgroundColor: null,
|
|
191
|
+
boxShadow: null,
|
|
192
|
+
overflow: null,
|
|
193
|
+
pointerEvents: 'none',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const triangleStyle = {
|
|
197
|
+
position: 'absolute',
|
|
198
|
+
width: '0px',
|
|
199
|
+
height: '0px',
|
|
200
|
+
boxSizing: 'border-box',
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
switch (triangle) {
|
|
204
|
+
case 'bottom':
|
|
205
|
+
triangleStyle.top = height;
|
|
206
|
+
triangleStyle.left = `calc(50% - ${triangleSize})`;
|
|
207
|
+
triangleStyle.borderTop = `${triangleSize} solid #fff`;
|
|
208
|
+
triangleStyle.borderBottom = `${triangleSize} solid transparent`;
|
|
209
|
+
triangleStyle.borderLeft = `${triangleSize} solid transparent`;
|
|
210
|
+
triangleStyle.borderRight = `${triangleSize} solid transparent`;
|
|
211
|
+
break;
|
|
212
|
+
case 'top':
|
|
213
|
+
triangleStyle.top = px(-2 * n(triangleSize));
|
|
214
|
+
triangleStyle.left = `calc(50% - ${triangleSize})`;
|
|
215
|
+
triangleStyle.borderTop = `${triangleSize} solid transparent`;
|
|
216
|
+
triangleStyle.borderBottom = `${triangleSize} solid #fff`;
|
|
217
|
+
triangleStyle.borderLeft = `${triangleSize} solid transparent`;
|
|
218
|
+
triangleStyle.borderRight = `${triangleSize} solid transparent`;
|
|
219
|
+
break;
|
|
220
|
+
case 'left':
|
|
221
|
+
triangleStyle.top = `calc(50% - ${triangleSize})`;
|
|
222
|
+
triangleStyle.left = px(-2 * n(triangleSize));
|
|
223
|
+
triangleStyle.borderTop = `${triangleSize} solid transparent`;
|
|
224
|
+
triangleStyle.borderBottom = `${triangleSize} solid transparent`;
|
|
225
|
+
triangleStyle.borderLeft = `${triangleSize} solid transparent`;
|
|
226
|
+
triangleStyle.borderRight = `${triangleSize} solid #fff`;
|
|
227
|
+
break;
|
|
228
|
+
case 'right':
|
|
229
|
+
triangleStyle.top = `calc(50% - ${triangleSize})`;
|
|
230
|
+
triangleStyle.left = width;
|
|
231
|
+
triangleStyle.borderTop = `${triangleSize} solid transparent`;
|
|
232
|
+
triangleStyle.borderBottom = `${triangleSize} solid transparent`;
|
|
233
|
+
triangleStyle.borderLeft = `${triangleSize} solid #fff`;
|
|
234
|
+
triangleStyle.borderRight = `${triangleSize} solid transparent`;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/******************************************************************************/
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
popupContainer,
|
|
242
|
+
window,
|
|
243
|
+
windowTriangle,
|
|
244
|
+
triangle: triangleStyle,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/******************************************************************************/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as styles from './styles';
|
|
3
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
4
|
+
|
|
5
|
+
/******************************************************************************/
|
|
6
|
+
|
|
7
|
+
function isInside(rect, x, y) {
|
|
8
|
+
if (rect && rect.left < rect.right && rect.top < rect.bottom) {
|
|
9
|
+
return (
|
|
10
|
+
x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
|
|
11
|
+
);
|
|
12
|
+
} else {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/******************************************************************************/
|
|
18
|
+
|
|
19
|
+
export default class PopupContainer extends Widget {
|
|
20
|
+
constructor() {
|
|
21
|
+
super(...arguments);
|
|
22
|
+
this.styles = styles;
|
|
23
|
+
this.onBackgroundClick = this.onBackgroundClick.bind(this);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onBackgroundClick(e) {
|
|
27
|
+
if (!this.props.enableBackgroundClickForCancel) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const rect = this.divWindow.getBoundingClientRect();
|
|
32
|
+
if (!isInside(rect, e.clientX, e.clientY)) {
|
|
33
|
+
// If the mouse is outside the window, close it.
|
|
34
|
+
this.props.onClose();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
renderTriangle() {
|
|
39
|
+
if (!this.props.triangle) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={this.styles.classNames.windowTriangle}>
|
|
45
|
+
<div className={this.styles.classNames.triangle} />
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
render() {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
className={this.styles.classNames.popupContainer}
|
|
54
|
+
onMouseDown={this.onBackgroundClick}
|
|
55
|
+
onTouchStart={this.onBackgroundClick}
|
|
56
|
+
>
|
|
57
|
+
<div
|
|
58
|
+
ref={(node) => (this.divWindow = node)}
|
|
59
|
+
className={this.styles.classNames.window}
|
|
60
|
+
>
|
|
61
|
+
{this.props.children}
|
|
62
|
+
</div>
|
|
63
|
+
{this.renderTriangle()}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/******************************************************************************/
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const goblinName = path.basename(module.parent.filename, '.js');
|
|
5
|
+
const Goblin = require('xcraft-core-goblin');
|
|
6
|
+
|
|
7
|
+
/******************************************************************************/
|
|
8
|
+
|
|
9
|
+
// Define initial logic values
|
|
10
|
+
const logicState = {
|
|
11
|
+
id: goblinName,
|
|
12
|
+
popup: null,
|
|
13
|
+
params: null,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/******************************************************************************/
|
|
17
|
+
|
|
18
|
+
const logicHandlers = {
|
|
19
|
+
create: (state, action) => {
|
|
20
|
+
return state.set('id', action.get('id'));
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
show: (state, action) => {
|
|
24
|
+
return state
|
|
25
|
+
.set('popup', action.get('popup'))
|
|
26
|
+
.set('params', action.get('params'));
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
hide: (state) => {
|
|
30
|
+
return state.set('popup', null).set('params', null);
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
setParams: (state, action) => {
|
|
34
|
+
return state.merge('params', action.get('params'));
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/******************************************************************************/
|
|
39
|
+
|
|
40
|
+
Goblin.registerQuest(goblinName, 'create', async function (
|
|
41
|
+
quest,
|
|
42
|
+
id,
|
|
43
|
+
desktopId,
|
|
44
|
+
labId
|
|
45
|
+
) {
|
|
46
|
+
quest.goblin.setX('desktopId', desktopId);
|
|
47
|
+
quest.goblin.setX('labId', labId);
|
|
48
|
+
|
|
49
|
+
await quest.warehouse.subscribe({
|
|
50
|
+
feed: desktopId,
|
|
51
|
+
branches: [id],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
quest.do({id});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/******************************************************************************/
|
|
58
|
+
|
|
59
|
+
Goblin.registerQuest(goblinName, 'show', async function (quest, popup, params) {
|
|
60
|
+
await quest.me.showWindow();
|
|
61
|
+
quest.do({popup, params});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
Goblin.registerQuest(goblinName, 'prompt', async function (
|
|
65
|
+
quest,
|
|
66
|
+
popup,
|
|
67
|
+
params
|
|
68
|
+
) {
|
|
69
|
+
await quest.me.showWindow();
|
|
70
|
+
const result = await quest.sub.callAndWait(async function () {
|
|
71
|
+
await quest.me.show({popup, params});
|
|
72
|
+
}, `*::*<${popup}.done>`);
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
Goblin.registerQuest(goblinName, 'hide', function (quest, result) {
|
|
78
|
+
const state = quest.goblin.getState();
|
|
79
|
+
const popup = state.get('popup');
|
|
80
|
+
quest.evt(`<${popup}.done>`, result);
|
|
81
|
+
quest.do();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
Goblin.registerQuest(goblinName, 'setParams', function (quest, params) {
|
|
85
|
+
quest.do({params});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
Goblin.registerQuest(goblinName, 'showWindow', async function (quest) {
|
|
89
|
+
const labId = quest.goblin.getX('labId');
|
|
90
|
+
if (!labId) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const winId = `wm@${labId}`;
|
|
94
|
+
try {
|
|
95
|
+
const wmAPI = quest.getAPI(winId);
|
|
96
|
+
await wmAPI.moveToFront();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
const err = e.stack || e.message || e;
|
|
99
|
+
quest.log.warn(
|
|
100
|
+
`Unable to call goblin-wm API ! showWindow works only in electron app ! ${err}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
Goblin.registerQuest(goblinName, 'delete', function (quest) {});
|
|
106
|
+
|
|
107
|
+
/******************************************************************************/
|
|
108
|
+
module.exports = Goblin.configure(goblinName, logicState, logicHandlers);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
+
import PopupContainer from 'goblin-gadgets/widgets/popup-container/widget';
|
|
4
|
+
|
|
5
|
+
/******************************************************************************/
|
|
6
|
+
|
|
7
|
+
function getAttachPoint(rect, shift, mode) {
|
|
8
|
+
let x, y;
|
|
9
|
+
|
|
10
|
+
switch (mode.x) {
|
|
11
|
+
case 'left':
|
|
12
|
+
x = rect.get('left');
|
|
13
|
+
break;
|
|
14
|
+
case 'right':
|
|
15
|
+
x = rect.get('right');
|
|
16
|
+
break;
|
|
17
|
+
case 'center':
|
|
18
|
+
x = (rect.get('left') + rect.get('right')) / 2;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (mode.y) {
|
|
23
|
+
case 'top':
|
|
24
|
+
y = rect.get('top');
|
|
25
|
+
break;
|
|
26
|
+
case 'bottom':
|
|
27
|
+
y = rect.get('bottom');
|
|
28
|
+
break;
|
|
29
|
+
case 'center':
|
|
30
|
+
y = (rect.gett('top') + rect.get('bottom')) / 2;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
x += shift.x;
|
|
35
|
+
y += shift.y;
|
|
36
|
+
|
|
37
|
+
return {x, y};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/******************************************************************************/
|
|
41
|
+
|
|
42
|
+
class PopupDispatcher extends Widget {
|
|
43
|
+
constructor() {
|
|
44
|
+
super(...arguments);
|
|
45
|
+
this.handleClose = this.handleClose.bind(this);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
handleClose(item) {
|
|
49
|
+
this.doFor(this.props.id, 'hide', {popup: item.popup});
|
|
50
|
+
}
|
|
51
|
+
/******************************************************************************/
|
|
52
|
+
|
|
53
|
+
renderPopup(item, index) {
|
|
54
|
+
const {id, popup, params} = this.props;
|
|
55
|
+
|
|
56
|
+
const show = popup === item.popup;
|
|
57
|
+
|
|
58
|
+
if (params?.get('parentRect') && item.attachMode) {
|
|
59
|
+
item.attachPoint = getAttachPoint(
|
|
60
|
+
params.get('parentRect'),
|
|
61
|
+
item.attachShift,
|
|
62
|
+
item.attachMode
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<PopupContainer
|
|
68
|
+
key={index}
|
|
69
|
+
visibility={show ? 'show' : 'hidden'}
|
|
70
|
+
size={item.size}
|
|
71
|
+
origin={item.origin}
|
|
72
|
+
attach={item.attach}
|
|
73
|
+
width={item.width}
|
|
74
|
+
height={item.height}
|
|
75
|
+
heightStrategy={item.heightStrategy}
|
|
76
|
+
marginTopBottom={item.marginTopBottom}
|
|
77
|
+
screenBackground={item.screenBackground}
|
|
78
|
+
attachPoint={item.attachPoint}
|
|
79
|
+
triangle={item.triangle}
|
|
80
|
+
triangleSize="16px"
|
|
81
|
+
transitionScope={item.transitionScope}
|
|
82
|
+
enableBackgroundClickForCancel={item.enableBackgroundClickForCancel}
|
|
83
|
+
onClose={() => this.handleClose(item)}
|
|
84
|
+
>
|
|
85
|
+
{item.render({id, popup, params})}
|
|
86
|
+
</PopupContainer>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
render() {
|
|
91
|
+
return this.props.popups.map((item, index) =>
|
|
92
|
+
this.renderPopup(item, index)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/******************************************************************************/
|
|
98
|
+
|
|
99
|
+
export default Widget.connect((state, props) => {
|
|
100
|
+
const serviceState = state.get(`backend.${props.id}`);
|
|
101
|
+
if (!serviceState) {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const popup = serviceState.get('popup');
|
|
106
|
+
const params = serviceState.get('params');
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
popup,
|
|
110
|
+
params,
|
|
111
|
+
};
|
|
112
|
+
})(PopupDispatcher);
|
|
@@ -55,6 +55,7 @@ class TabNavigationLogic extends Elf.Spirit {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
class TabNavigation extends Elf {
|
|
58
|
+
logic = Elf.getLogic(TabNavigationLogic);
|
|
58
59
|
state = new TabNavigationState();
|
|
59
60
|
|
|
60
61
|
/** @type {NavigationViews} */
|
|
@@ -72,14 +73,14 @@ class TabNavigation extends Elf {
|
|
|
72
73
|
async create(id, desktopId, views) {
|
|
73
74
|
this.desktopId = desktopId;
|
|
74
75
|
this.views = views;
|
|
75
|
-
this.
|
|
76
|
+
this.logic.create(id);
|
|
76
77
|
const firstTab = Object.keys(views)[0];
|
|
77
78
|
await this.setTab(firstTab);
|
|
78
79
|
return this;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
async change(path, newValue) {
|
|
82
|
-
this.
|
|
83
|
+
this.logic.change(path, newValue);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
async _loadService(tab) {
|
|
@@ -112,7 +113,7 @@ class TabNavigation extends Elf {
|
|
|
112
113
|
const view = this.views[tab];
|
|
113
114
|
const widget = view.widget;
|
|
114
115
|
const widgetProps = view.widgetProps;
|
|
115
|
-
this.
|
|
116
|
+
this.logic.setTab(tab, serviceId, widget, widgetProps);
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
async switchTab(reverse) {
|