goblin-magic 1.0.3

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.
Files changed (88) hide show
  1. package/.editorconfig +9 -0
  2. package/.zou-flow +2 -0
  3. package/eslint.config.js +65 -0
  4. package/magicNavigation.js +7 -0
  5. package/package.json +45 -0
  6. package/widgets/dialog/widget.js +78 -0
  7. package/widgets/element-helpers/element-has-direct-text.js +9 -0
  8. package/widgets/element-helpers/is-empty-area-element.js +17 -0
  9. package/widgets/element-helpers/is-flat-element.js +52 -0
  10. package/widgets/get-modifiers/get-modifiers.js +34 -0
  11. package/widgets/input-group/styles.js +74 -0
  12. package/widgets/input-group/widget.js +24 -0
  13. package/widgets/magic-action/styles.js +45 -0
  14. package/widgets/magic-action/widget.js +44 -0
  15. package/widgets/magic-background/bg-alps.jpg +0 -0
  16. package/widgets/magic-background/bg-fur.png +0 -0
  17. package/widgets/magic-background/bg-milkyway.png +0 -0
  18. package/widgets/magic-background/bg-space.jpg +0 -0
  19. package/widgets/magic-background/bg-synth.jpg +0 -0
  20. package/widgets/magic-background/bg-white.png +0 -0
  21. package/widgets/magic-background/styles.js +81 -0
  22. package/widgets/magic-background/widget.js +20 -0
  23. package/widgets/magic-box/styles.js +10 -0
  24. package/widgets/magic-box/widget.js +28 -0
  25. package/widgets/magic-box-old/styles.js +111 -0
  26. package/widgets/magic-box-old/widget.js +30 -0
  27. package/widgets/magic-button/styles.js +156 -0
  28. package/widgets/magic-button/widget.js +89 -0
  29. package/widgets/magic-checkbox/styles.js +116 -0
  30. package/widgets/magic-checkbox/widget.js +68 -0
  31. package/widgets/magic-color-field/styles.js +22 -0
  32. package/widgets/magic-color-field/widget.js +68 -0
  33. package/widgets/magic-date-field/styles.js +9 -0
  34. package/widgets/magic-date-field/widget.js +145 -0
  35. package/widgets/magic-datetime-field/styles.js +11 -0
  36. package/widgets/magic-datetime-field/widget.js +95 -0
  37. package/widgets/magic-dialog/styles.js +39 -0
  38. package/widgets/magic-dialog/widget.js +116 -0
  39. package/widgets/magic-div/styles.js +22 -0
  40. package/widgets/magic-div/widget.js +20 -0
  41. package/widgets/magic-emoji/styles.js +14 -0
  42. package/widgets/magic-emoji/widget.js +33 -0
  43. package/widgets/magic-emoji-picker/styles.js +21 -0
  44. package/widgets/magic-emoji-picker/widget.js +44 -0
  45. package/widgets/magic-inplace-input/styles.js +55 -0
  46. package/widgets/magic-inplace-input/widget.js +26 -0
  47. package/widgets/magic-input/styles.js +50 -0
  48. package/widgets/magic-input/widget.js +397 -0
  49. package/widgets/magic-label/styles.js +20 -0
  50. package/widgets/magic-label/widget.js +24 -0
  51. package/widgets/magic-navigation/service.js +1306 -0
  52. package/widgets/magic-navigation/styles.js +103 -0
  53. package/widgets/magic-navigation/view-context.js +15 -0
  54. package/widgets/magic-navigation/widget.js +540 -0
  55. package/widgets/magic-number-field/styles.js +10 -0
  56. package/widgets/magic-number-field/widget.js +103 -0
  57. package/widgets/magic-panel/styles.js +61 -0
  58. package/widgets/magic-panel/widget.js +63 -0
  59. package/widgets/magic-radio/styles.js +93 -0
  60. package/widgets/magic-radio/widget.js +74 -0
  61. package/widgets/magic-scroll/styles.js +22 -0
  62. package/widgets/magic-scroll/widget.js +20 -0
  63. package/widgets/magic-select/styles.js +16 -0
  64. package/widgets/magic-select/widget.js +134 -0
  65. package/widgets/magic-table/reducer.js +63 -0
  66. package/widgets/magic-table/styles.js +170 -0
  67. package/widgets/magic-table/widget.js +627 -0
  68. package/widgets/magic-tag/styles.js +32 -0
  69. package/widgets/magic-tag/widget.js +32 -0
  70. package/widgets/magic-text-field/styles.js +58 -0
  71. package/widgets/magic-text-field/widget.js +66 -0
  72. package/widgets/magic-time-field/styles.js +8 -0
  73. package/widgets/magic-time-field/widget.js +142 -0
  74. package/widgets/magic-timer/styles.js +30 -0
  75. package/widgets/magic-timer/widget.js +162 -0
  76. package/widgets/magic-zen/styles.js +61 -0
  77. package/widgets/magic-zen/widget.js +42 -0
  78. package/widgets/main-tabs/styles.js +106 -0
  79. package/widgets/main-tabs/widget.js +23 -0
  80. package/widgets/menu/styles.js +156 -0
  81. package/widgets/menu/test-menu.html +154 -0
  82. package/widgets/menu/widget.js +575 -0
  83. package/widgets/movable/widget.js +80 -0
  84. package/widgets/splitter/styles.js +57 -0
  85. package/widgets/splitter/widget.js +40 -0
  86. package/widgets/tab-layout/styles.js +31 -0
  87. package/widgets/tab-layout/widget.js +59 -0
  88. package/widgets/with-computed-size/widget.js +52 -0
@@ -0,0 +1,103 @@
1
+ export default function styles() {
2
+ const panels = {
3
+ 'width': '100%',
4
+ 'height': '100%',
5
+ 'minHeight': 0, // allow to shrink
6
+ 'minWidth': 0, // allow to shrink
7
+ 'display': 'flex',
8
+ 'flexDirection': 'row',
9
+
10
+ '& > *': {
11
+ flexGrow: 1,
12
+ },
13
+
14
+ '& .layout-pane': {
15
+ 'display': 'flex',
16
+ 'flexDirection': 'row',
17
+ '& > *': {
18
+ flexGrow: 1,
19
+ },
20
+ },
21
+ };
22
+
23
+ const panel = {
24
+ outline: 'none',
25
+ };
26
+
27
+ const panelTop = {
28
+ display: 'flex',
29
+ flexDirection: 'row',
30
+ justifyContent: 'space-between',
31
+ };
32
+
33
+ const tabName = {
34
+ 'display': 'flex',
35
+ 'whiteSpace': 'nowrap',
36
+ 'overflow': 'hidden',
37
+ 'textOverflow': 'ellipsis',
38
+ 'position': 'relative',
39
+ 'minHeight': '1.4em',
40
+ '& > svg': {
41
+ minWidth: '1.3em',
42
+ },
43
+ '& > span': {
44
+ overflow: 'hidden',
45
+ textOverflow: 'ellipsis',
46
+ },
47
+ };
48
+
49
+ const closeButton = {
50
+ 'display': 'flex',
51
+ 'justifyContent': 'center',
52
+ 'width': '19px',
53
+ 'height': '20px',
54
+ 'flexShrink': 0,
55
+ 'marginLeft': '8px',
56
+ 'borderRadius': '3px',
57
+ 'opacity': 0.6,
58
+
59
+ ':hover': {
60
+ opacity: 0.9,
61
+ backgroundColor: 'color-mix(in srgb, var(--text-color), transparent 80%)',
62
+ },
63
+
64
+ ':active': {
65
+ opacity: 0.6,
66
+ },
67
+ };
68
+
69
+ const tabMenuButton = {
70
+ 'fontSize': '14px',
71
+ // 'marginLeft': '10px',
72
+ 'padding': '5px',
73
+ 'margin': '-5px -5px -5px 5px',
74
+ 'opacity': 0.3,
75
+ ':hover': {
76
+ opacity: 0.8,
77
+ },
78
+ };
79
+
80
+ const view = {
81
+ 'display': 'flex',
82
+ 'flexDirection': 'column',
83
+ '& > *': {
84
+ flexGrow: 1,
85
+ minHeight: 0, // allow to shrink
86
+ },
87
+
88
+ '&[data-visible=false]': {
89
+ display: 'none',
90
+ // contentVisibility: 'hidden', // TODO try to use 'content-visibility' instead of 'display'
91
+ },
92
+ };
93
+
94
+ return {
95
+ panels,
96
+ panel,
97
+ panelTop,
98
+ tabName,
99
+ closeButton,
100
+ tabMenuButton,
101
+ view,
102
+ };
103
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+
3
+ const {createContext} = require('react');
4
+
5
+ const ViewContext = createContext();
6
+
7
+ export default ViewContext;
8
+
9
+ export function withView(Component) {
10
+ return (props) => (
11
+ <ViewContext.Consumer>
12
+ {(view) => <Component view={view} {...props} />}
13
+ </ViewContext.Consumer>
14
+ );
15
+ }
@@ -0,0 +1,540 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget/index.js';
3
+ import * as styles from './styles.js';
4
+ import withC from 'goblin-laboratory/widgets/connect-helpers/with-c.js';
5
+ import C from 'goblin-laboratory/widgets/connect-helpers/c.js';
6
+ import WithModel from 'goblin-laboratory/widgets/with-model/widget.js';
7
+ import ErrorHandler from 'goblin-laboratory/widgets/error-handler/widget.js';
8
+ import TabLayout from '../tab-layout/widget.js';
9
+ import MainTabs from '../main-tabs/widget.js';
10
+ import MagicDialog from '../magic-dialog/widget.js';
11
+ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
12
+ import {
13
+ faColumns,
14
+ faClone,
15
+ faTimesCircle,
16
+ faArrowLeft,
17
+ faArrowRight,
18
+ faPlus,
19
+ faPaintBrush,
20
+ } from '@fortawesome/free-solid-svg-icons';
21
+ import Menu from '../menu/widget.js';
22
+ import getModifiers, {getPlatform} from '../get-modifiers/get-modifiers.js';
23
+ import ViewContext from './view-context.js';
24
+ import Icon from '@mdi/react';
25
+ import {mdiClose} from '@mdi/js';
26
+ import Splitter from '../splitter/widget.js';
27
+
28
+ let MagicNavigationView = class extends Widget {
29
+ constructor() {
30
+ super(...arguments);
31
+ this.styles = styles;
32
+ }
33
+
34
+ render() {
35
+ const {view, viewId, widgets, visible} = this.props;
36
+ if (!view) {
37
+ return null;
38
+ }
39
+ const widget = view.get('widget') || view.get('serviceId').split('@', 1)[0];
40
+ if (!(widget in widgets)) {
41
+ throw new Error(`Unknown widget '${widget}'`);
42
+ }
43
+ const widgetProps = view.get('widgetProps')?.toObject();
44
+ const serviceId = view.get('serviceId');
45
+ const Component = widgets[widget];
46
+ return (
47
+ <div className={this.styles.classNames.view} data-visible={visible}>
48
+ <WithModel model={serviceId ? `backend.${serviceId}` : ''}>
49
+ <ViewContext.Provider value={view.set('id', viewId)}>
50
+ <ErrorHandler big>
51
+ <Component id={serviceId} viewId={viewId} {...widgetProps} />
52
+ </ErrorHandler>
53
+ </ViewContext.Provider>
54
+ </WithModel>
55
+ </div>
56
+ );
57
+ }
58
+ };
59
+ MagicNavigationView = withC(MagicNavigationView);
60
+
61
+ let MagicNavigationViews = class extends Widget {
62
+ constructor() {
63
+ super(...arguments);
64
+ }
65
+
66
+ render() {
67
+ const tabIds = this.props.tabIds;
68
+ const currentTabId = this.props.currentTabId;
69
+ if (this.props.unmountHidden) {
70
+ return (
71
+ <MagicNavigationView
72
+ id={this.props.id}
73
+ viewId={currentTabId}
74
+ view={C(`.tabs.${currentTabId}`)}
75
+ widgets={this.props.widgets}
76
+ visible={true}
77
+ />
78
+ );
79
+ }
80
+ return tabIds.map((tabId) => (
81
+ <MagicNavigationView
82
+ key={tabId}
83
+ id={this.props.id}
84
+ viewId={tabId}
85
+ view={C(`.tabs.${tabId}`)}
86
+ widgets={this.props.widgets}
87
+ visible={tabId === currentTabId}
88
+ />
89
+ ));
90
+ }
91
+ };
92
+ MagicNavigationViews = withC(MagicNavigationViews);
93
+
94
+ class MagicNavigationTabNC extends Widget {
95
+ constructor() {
96
+ super(...arguments);
97
+ this.styles = styles;
98
+ this.close = this.close.bind(this);
99
+ this.duplicateTab = this.duplicateTab.bind(this);
100
+ this.highlightTab = this.highlightTab.bind(this);
101
+ this.moveTabToNewWindow = this.moveTabToNewWindow.bind(this);
102
+ this.moveTabToRightPanel = this.moveTabToRightPanel.bind(this);
103
+ this.moveTabToLeftPanel = this.moveTabToLeftPanel.bind(this);
104
+ }
105
+
106
+ close(event) {
107
+ this.doFor('magicNavigation@main', 'closeTab', {tabId: this.props.value});
108
+ event.stopPropagation();
109
+ }
110
+
111
+ duplicateTab() {
112
+ this.doFor('magicNavigation@main', 'duplicateTab', {
113
+ tabId: this.props.value,
114
+ });
115
+ }
116
+
117
+ highlightTab() {
118
+ this.doFor('magicNavigation@main', 'highlightTab', {
119
+ tabId: this.props.value,
120
+ });
121
+ }
122
+
123
+ moveTabToNewWindow() {
124
+ this.doFor('magicNavigation@main', 'moveTabToNewWindow', {
125
+ tabId: this.props.value,
126
+ });
127
+ }
128
+
129
+ moveTabToRightPanel() {
130
+ this.doFor('magicNavigation@main', 'moveTabToPanel', {
131
+ tabId: this.props.value,
132
+ nextPanel: true,
133
+ });
134
+ }
135
+
136
+ moveTabToLeftPanel() {
137
+ this.doFor('magicNavigation@main', 'moveTabToPanel', {
138
+ tabId: this.props.value,
139
+ nextPanel: false,
140
+ });
141
+ }
142
+
143
+ render() {
144
+ const {tab, value, widgets, ...props} = this.props;
145
+ if (!tab) {
146
+ return null;
147
+ }
148
+
149
+ const widget = tab.get('widget') || tab.get('serviceId').split('@', 1)[0];
150
+ const TabName = widgets[widget]?.TabName;
151
+ const highlighted = tab.get('highlighted');
152
+ return (
153
+ <Menu>
154
+ <div data-value={value} data-highlighted={highlighted} {...props}>
155
+ <div className={this.styles.classNames.tabName}>
156
+ <ErrorHandler>
157
+ {TabName ? <TabName tab={tab} /> : widget}
158
+ </ErrorHandler>
159
+ </div>
160
+ {/* <Menu.Button
161
+ Component="a"
162
+ className={this.styles.classNames.tabMenuButton}
163
+ >
164
+ <FontAwesomeIcon icon={faBars} />
165
+ </Menu.Button> */}
166
+ <a
167
+ className={this.styles.classNames.closeButton}
168
+ onClick={this.close}
169
+ >
170
+ <Icon path={mdiClose} />
171
+ </a>
172
+
173
+ <Menu.Content>
174
+ <Menu.Submenu
175
+ item={
176
+ <>
177
+ <FontAwesomeIcon icon={faColumns} /> Déplacer l'onglet
178
+ </>
179
+ }
180
+ >
181
+ <Menu.Item onPointerUp={this.moveTabToLeftPanel}>
182
+ <FontAwesomeIcon icon={faArrowLeft} /> Vers le panneau de gauche
183
+ </Menu.Item>
184
+ <Menu.Item onPointerUp={this.moveTabToRightPanel}>
185
+ <FontAwesomeIcon icon={faArrowRight} /> Vers le panneau de
186
+ droite
187
+ </Menu.Item>
188
+ <Menu.Item onPointerUp={this.moveTabToNewWindow}>
189
+ <FontAwesomeIcon icon={faPlus} /> Vers une nouvelle fenêtre
190
+ </Menu.Item>
191
+ {/* <Menu.Item>Vers la fenêtre ...</Menu.Item> */}
192
+ </Menu.Submenu>
193
+ <Menu.Item onPointerUp={this.highlightTab}>
194
+ <FontAwesomeIcon icon={faPaintBrush} /> Mettre en évidence
195
+ </Menu.Item>
196
+ <Menu.Item onPointerUp={this.duplicateTab}>
197
+ <FontAwesomeIcon icon={faClone} /> Dupliquer l&apos;onglet
198
+ </Menu.Item>
199
+ <Menu.Item onPointerUp={this.close}>
200
+ <FontAwesomeIcon icon={faTimesCircle} /> Fermer l&apos;onglet
201
+ </Menu.Item>
202
+ </Menu.Content>
203
+ </div>
204
+ </Menu>
205
+ );
206
+ }
207
+ }
208
+ const MagicNavigationTab = withC(MagicNavigationTabNC);
209
+
210
+ let MagicNavigationTabs = class extends Widget {
211
+ constructor() {
212
+ super(...arguments);
213
+ this.handleKeyDown = this.handleKeyDown.bind(this);
214
+ this.setTab = this.setTab.bind(this);
215
+ this.handleAuxClick = this.handleAuxClick.bind(this);
216
+ }
217
+
218
+ handleKeyDown(event) {
219
+ if (event.key === 'ArrowLeft') {
220
+ this.doFor('magicNavigation@main', 'switchTab', {reverse: true});
221
+ } else if (event.key === 'ArrowRight') {
222
+ this.doFor('magicNavigation@main', 'switchTab');
223
+ }
224
+ }
225
+
226
+ setTab(tabId) {
227
+ this.doFor('magicNavigation@main', 'activateTab', {tabId});
228
+ }
229
+
230
+ handleAuxClick(event) {
231
+ if (event.button === 1) {
232
+ // Middle click
233
+ const tabId = event.currentTarget.dataset.value;
234
+ this.doFor('magicNavigation@main', 'closeTab', {tabId});
235
+ }
236
+ }
237
+
238
+ render() {
239
+ const tabIds = this.props.tabIds;
240
+ const currentTabId = this.props.currentTabId;
241
+ return (
242
+ <MainTabs
243
+ currentTab={currentTabId}
244
+ onTabClick={this.setTab}
245
+ tabIndex="0"
246
+ onKeyDown={this.handleKeyDown}
247
+ >
248
+ {tabIds.map((tabId) => (
249
+ <MagicNavigationTab
250
+ key={tabId}
251
+ value={tabId}
252
+ tab={C(`.tabs.${tabId}`)}
253
+ onAuxClick={this.handleAuxClick}
254
+ widgets={this.props.widgets}
255
+ />
256
+ ))}
257
+ </MainTabs>
258
+ );
259
+ }
260
+ };
261
+ MagicNavigationTabs = withC(MagicNavigationTabs);
262
+
263
+ let MagicNavigationPanel = class extends Widget {
264
+ constructor() {
265
+ super(...arguments);
266
+ this.styles = styles;
267
+ }
268
+
269
+ handleFocus = () => {
270
+ const {id, windowId, panelId} = this.props;
271
+ const activePanelId = this.getBackendState(id)
272
+ .get('windows')
273
+ .get(windowId)
274
+ .get('activePanelId');
275
+ if (activePanelId !== panelId) {
276
+ this.doFor(id, 'activatePanel', {
277
+ panelId,
278
+ });
279
+ }
280
+ };
281
+
282
+ render() {
283
+ const {panel, topRight} = this.props;
284
+ return (
285
+ <TabLayout
286
+ tabIndex="0"
287
+ onFocusCapture={this.handleFocus}
288
+ className={this.styles.classNames.panel}
289
+ >
290
+ <div className={this.styles.classNames.panelTop}>
291
+ <MagicNavigationTabs
292
+ id={this.props.id}
293
+ currentTabId={panel.get('currentTabId')}
294
+ tabIds={panel.get('tabIds')}
295
+ widgets={this.props.widgets}
296
+ />
297
+ {topRight}
298
+ </div>
299
+ <MagicNavigationViews
300
+ id={this.props.id}
301
+ tabIds={panel.get('tabIds')}
302
+ currentTabId={panel.get('currentTabId')}
303
+ widgets={this.props.widgets}
304
+ unmountHidden={false}
305
+ />
306
+ </TabLayout>
307
+ );
308
+ }
309
+ };
310
+ MagicNavigationPanel = withC(MagicNavigationPanel);
311
+
312
+ function MultiSplitter({children}) {
313
+ const array = React.Children.toArray(children);
314
+ if (array.length === 1) {
315
+ return array[0];
316
+ }
317
+ const [first, ...other] = array;
318
+ return (
319
+ <Splitter
320
+ primaryMinSize={15}
321
+ secondaryMinSize={15}
322
+ secondaryInitialSize={Math.floor(100 - 100 / array.length)}
323
+ percentage={true}
324
+ >
325
+ {first}
326
+ {MultiSplitter({children: other})}
327
+ </Splitter>
328
+ );
329
+ }
330
+
331
+ let MagicNavigationPanels = class extends Widget {
332
+ constructor() {
333
+ super(...arguments);
334
+ this.styles = styles;
335
+ }
336
+
337
+ render() {
338
+ const {id, windowId, panelIds, widgets, topRight, rightPanel} = this.props;
339
+ if (!panelIds) {
340
+ return null; // Loading
341
+ }
342
+ const lastPanelIndex = panelIds.length - 1;
343
+ return (
344
+ <div className={this.styles.classNames.panels}>
345
+ <MultiSplitter key={panelIds.length}>
346
+ {panelIds.map((panelId, index) => (
347
+ <MagicNavigationPanel
348
+ key={panelId}
349
+ id={id}
350
+ windowId={windowId}
351
+ panelId={panelId}
352
+ panel={C(`.panels.${panelId}`)}
353
+ widgets={widgets}
354
+ topRight={index === lastPanelIndex ? topRight : null}
355
+ />
356
+ ))}
357
+ </MultiSplitter>
358
+ {rightPanel}
359
+ </div>
360
+ );
361
+ }
362
+ };
363
+ MagicNavigationPanels = withC(MagicNavigationPanels);
364
+
365
+ let MagicNavigationDialog = class extends Widget {
366
+ constructor() {
367
+ super(...arguments);
368
+ this.handleClose = this.handleClose.bind(this);
369
+ }
370
+
371
+ handleClose(event) {
372
+ const returnValue = event.currentTarget.returnValue;
373
+ const dialogId = this.props.dialogId;
374
+ this.doFor('magicNavigation@main', 'closeDialog', {
375
+ dialogId,
376
+ result: returnValue !== '' ? returnValue : undefined,
377
+ });
378
+ }
379
+
380
+ render() {
381
+ const dialogId = this.props.dialogId;
382
+ const widgetProps = this.props.view.get('widgetProps')?.toObject();
383
+ return (
384
+ <MagicDialog modal={widgetProps?.modal} open onClose={this.handleClose}>
385
+ <MagicNavigationView
386
+ id={this.props.id}
387
+ viewId={dialogId}
388
+ view={this.props.view}
389
+ widgets={this.props.widgets}
390
+ />
391
+ </MagicDialog>
392
+ );
393
+ }
394
+ };
395
+ MagicNavigationDialog = withC(MagicNavigationDialog);
396
+
397
+ let MagicNavigationDialogs = class extends Widget {
398
+ constructor() {
399
+ super(...arguments);
400
+ this.styles = styles;
401
+ }
402
+
403
+ render() {
404
+ const {dialogIds, widgets} = this.props;
405
+ if (!dialogIds) {
406
+ return null; // Loading
407
+ }
408
+ return dialogIds.map((dialogId) => (
409
+ <MagicNavigationDialog
410
+ key={dialogId}
411
+ dialogId={dialogId}
412
+ view={C(`.tabs.${dialogId}`)}
413
+ widgets={widgets}
414
+ />
415
+ ));
416
+ }
417
+ };
418
+ MagicNavigationDialogs = withC(MagicNavigationDialogs);
419
+
420
+ class MagicNavigation extends Widget {
421
+ constructor() {
422
+ super(...arguments);
423
+ this.handleKeyDown = this.handleKeyDown.bind(this);
424
+ }
425
+
426
+ componentDidMount() {
427
+ window.addEventListener('keydown', this.handleKeyDown);
428
+ }
429
+
430
+ componentWillUnmount() {
431
+ window.removeEventListener('keydown', this.handleKeyDown);
432
+ }
433
+
434
+ handleKeyDown(event) {
435
+ const platform = getPlatform(navigator.userAgent);
436
+ const modifiers = getModifiers(event);
437
+ if (modifiers.ctrlKey) {
438
+ if (event.key === 'w') {
439
+ this.closeCurrentTab();
440
+ } else if (event.key === 'o') {
441
+ this.openById();
442
+ }
443
+ }
444
+ if (modifiers.ctrlKey || modifiers.altKey) {
445
+ if (
446
+ [
447
+ 'Digit1',
448
+ 'Digit2',
449
+ 'Digit3',
450
+ 'Digit4',
451
+ 'Digit5',
452
+ 'Digit6',
453
+ 'Digit7',
454
+ 'Digit8',
455
+ 'Digit9',
456
+ ].includes(event.code)
457
+ ) {
458
+ this.activateTabIndex(Number(event.key));
459
+ }
460
+ }
461
+ if (event.ctrlKey) {
462
+ // Platform independent
463
+ if (event.key === 'Tab') {
464
+ if (event.shiftKey) {
465
+ this.reverseSwitchTab();
466
+ } else {
467
+ this.switchTab();
468
+ }
469
+ }
470
+ }
471
+ if (
472
+ (platform !== 'macos' && modifiers.altKey) ||
473
+ (platform === 'macos' && modifiers.metaKey)
474
+ ) {
475
+ if (event.key === 'ArrowLeft') {
476
+ this.backCurrentTab();
477
+ }
478
+ }
479
+ }
480
+
481
+ openById = () => {
482
+ this.doFor('magicNavigation@main', 'openDialog', {
483
+ view: {
484
+ service: 'openWizard',
485
+ },
486
+ parentId: this.context.desktopId,
487
+ });
488
+ };
489
+
490
+ closeCurrentTab() {
491
+ this.doFor('magicNavigation@main', 'closeCurrentTab');
492
+ }
493
+
494
+ switchTab() {
495
+ this.doFor('magicNavigation@main', 'switchTab');
496
+ }
497
+
498
+ reverseSwitchTab() {
499
+ this.doFor('magicNavigation@main', 'switchTab', {reverse: true});
500
+ }
501
+
502
+ activateTabIndex(index) {
503
+ this.doFor('magicNavigation@main', 'activateTabIndex', {index});
504
+ }
505
+
506
+ backCurrentTab() {
507
+ this.doFor('magicNavigation@main', 'backCurrentTab');
508
+ }
509
+
510
+ render() {
511
+ const {id, widgets, windowId, topRight, rightPanel} = this.props;
512
+ const newWidgets = {
513
+ ...Object.fromEntries(
514
+ Object.entries(widgets).map(([name, widget]) => [
515
+ name[0].toLowerCase() + name.slice(1),
516
+ widget,
517
+ ])
518
+ ),
519
+ ...widgets,
520
+ };
521
+ return (
522
+ <WithModel model={`backend.${id}`}>
523
+ <MagicNavigationPanels
524
+ id={id}
525
+ windowId={windowId}
526
+ widgets={newWidgets}
527
+ panelIds={C(`.windows.${windowId}.panelIds`)}
528
+ topRight={topRight}
529
+ rightPanel={rightPanel}
530
+ />
531
+ <MagicNavigationDialogs
532
+ widgets={newWidgets}
533
+ dialogIds={C(`.windows.${windowId}.dialogIds`)}
534
+ />
535
+ </WithModel>
536
+ );
537
+ }
538
+ }
539
+
540
+ export default MagicNavigation;
@@ -0,0 +1,10 @@
1
+ export default function styles() {
2
+ const numberField = {
3
+ '&[class]' /* increase specificity */: {
4
+ width: '90px',
5
+ },
6
+ };
7
+ return {
8
+ numberField,
9
+ };
10
+ }