goblin-desktop 4.2.0 → 4.2.2

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 (144) hide show
  1. package/.editorconfig +9 -9
  2. package/.zou-flow +2 -2
  3. package/LICENSE +21 -21
  4. package/README.md +108 -108
  5. package/builders/builders.js +3 -3
  6. package/builders/wizard.js +462 -462
  7. package/contexts.js +13 -13
  8. package/desktop-manager.js +10 -10
  9. package/desktop-window.js +13 -13
  10. package/desktop.js +13 -13
  11. package/eslint.config.js +65 -65
  12. package/lib/service.js +137 -137
  13. package/package.json +48 -48
  14. package/password-wizard.js +24 -24
  15. package/quest-run-wizard.js +24 -24
  16. package/taskbar.js +13 -13
  17. package/widgets/audio/sfx.js +177 -177
  18. package/widgets/board/view.js +37 -37
  19. package/widgets/board/widget.js +65 -65
  20. package/widgets/contexts/logic-handlers.js +36 -36
  21. package/widgets/contexts/service.js +40 -40
  22. package/widgets/contexts/view.js +21 -21
  23. package/widgets/contexts/widget.js +147 -147
  24. package/widgets/datagrid/datagrid-entity.js +82 -82
  25. package/widgets/datagrid/datagrid-headers.js +274 -274
  26. package/widgets/datagrid/styles.js +13 -13
  27. package/widgets/datagrid/widget.js +192 -192
  28. package/widgets/datagrid-cell/styles.js +52 -52
  29. package/widgets/datagrid-cell/widget.js +41 -41
  30. package/widgets/datagrid-item/styles.js +15 -15
  31. package/widgets/datagrid-item/widget.js +74 -74
  32. package/widgets/default/view.js +160 -160
  33. package/widgets/desktop/compensator.js +9 -9
  34. package/widgets/desktop/logic-handlers.js +243 -243
  35. package/widgets/desktop/reducer.js +35 -35
  36. package/widgets/desktop/service.js +858 -858
  37. package/widgets/desktop/styles.js +28 -28
  38. package/widgets/desktop/widget.js +191 -191
  39. package/widgets/desktop-clock/styles.js +69 -69
  40. package/widgets/desktop-clock/widget.js +387 -387
  41. package/widgets/desktop-clock-clock/styles.js +56 -56
  42. package/widgets/desktop-clock-clock/widget.js +96 -96
  43. package/widgets/desktop-clock-menu/styles.js +129 -129
  44. package/widgets/desktop-clock-menu/widget.js +63 -63
  45. package/widgets/desktop-connection-status/reducer.js +15 -15
  46. package/widgets/desktop-connection-status/styles.js +44 -44
  47. package/widgets/desktop-connection-status/widget.js +134 -134
  48. package/widgets/desktop-content/widget.js +68 -68
  49. package/widgets/desktop-footer/reducer.js +31 -31
  50. package/widgets/desktop-footer/styles.js +36 -36
  51. package/widgets/desktop-footer/widget.js +52 -52
  52. package/widgets/desktop-monitors/styles.js +155 -155
  53. package/widgets/desktop-monitors/widget.js +272 -272
  54. package/widgets/desktop-notebook/styles.js +155 -155
  55. package/widgets/desktop-notebook/widget.js +252 -252
  56. package/widgets/desktop-notifications/styles.js +147 -147
  57. package/widgets/desktop-notifications/widget.js +231 -231
  58. package/widgets/desktop-scale/reducer.js +15 -15
  59. package/widgets/desktop-scale/styles.js +49 -49
  60. package/widgets/desktop-scale/widget.js +173 -172
  61. package/widgets/desktop-state-monitor/styles.js +72 -72
  62. package/widgets/desktop-state-monitor/widget.js +123 -123
  63. package/widgets/desktop-taskbar/widget.js +57 -57
  64. package/widgets/desktop-themes-menu/widget.js +121 -121
  65. package/widgets/desktop-topbar/widget.js +201 -201
  66. package/widgets/desktop-window/service.js +56 -56
  67. package/widgets/desktop-window/styles.js +22 -22
  68. package/widgets/desktop-window/widget.js +70 -70
  69. package/widgets/detail/compensator.js +17 -17
  70. package/widgets/detail/view.js +40 -40
  71. package/widgets/detail/widget.js +119 -119
  72. package/widgets/editor/widget.js +82 -82
  73. package/widgets/entity-alerts/styles.js +77 -77
  74. package/widgets/entity-alerts/widget.js +328 -328
  75. package/widgets/entity-list/styles.js +66 -66
  76. package/widgets/entity-list/view.js +36 -36
  77. package/widgets/entity-list/widget.js +209 -209
  78. package/widgets/entity-list-item/widget.js +68 -68
  79. package/widgets/entity-row/styles.js +105 -105
  80. package/widgets/entity-row/widget.js +524 -524
  81. package/widgets/entity-row-button/styles.js +46 -46
  82. package/widgets/entity-row-button/widget.js +57 -57
  83. package/widgets/entity-view/reducer.js +20 -20
  84. package/widgets/entity-view/styles.js +90 -90
  85. package/widgets/entity-view/widget.js +601 -601
  86. package/widgets/facet-checkbox/styles.js +17 -17
  87. package/widgets/facet-checkbox/widget.js +43 -43
  88. package/widgets/facet-filter/widget.js +94 -94
  89. package/widgets/facet-filter-add/styles.js +30 -30
  90. package/widgets/facet-filter-add/widget.js +105 -105
  91. package/widgets/facet-filter-button/styles.js +74 -74
  92. package/widgets/facet-filter-button/widget.js +214 -214
  93. package/widgets/facet-filter-list-dialog/styles.js +59 -59
  94. package/widgets/facet-filter-list-dialog/widget.js +273 -273
  95. package/widgets/facet-filter-list-dialog-footer/styles.js +22 -22
  96. package/widgets/facet-filter-list-dialog-footer/widget.js +105 -105
  97. package/widgets/facet-filter-range-dialog/styles.js +82 -82
  98. package/widgets/facet-filter-range-dialog/widget.js +399 -399
  99. package/widgets/facet-filter-range-dialog-footer/styles.js +22 -22
  100. package/widgets/facet-filter-range-dialog-footer/widget.js +182 -182
  101. package/widgets/gamepad/widget.js +75 -75
  102. package/widgets/helpers/facet-helpers.js +105 -105
  103. package/widgets/hinter/reducer.js +35 -35
  104. package/widgets/hinter/styles.js +79 -79
  105. package/widgets/hinter/view.js +32 -32
  106. package/widgets/hinter/widget.js +289 -291
  107. package/widgets/junction/styles.js +22 -22
  108. package/widgets/junction/widget.js +50 -50
  109. package/widgets/main-tab-menu/styles.js +17 -17
  110. package/widgets/main-tab-menu/widget.js +130 -130
  111. package/widgets/map/view.js +49 -49
  112. package/widgets/map/widget.js +64 -64
  113. package/widgets/monitor/reducer.js +15 -15
  114. package/widgets/monitor/widget.js +60 -60
  115. package/widgets/navigating-layer/widget.js +25 -25
  116. package/widgets/notifications-button/widget.js +44 -44
  117. package/widgets/password-wizard/service.js +53 -53
  118. package/widgets/password-wizard/ui.js +66 -66
  119. package/widgets/plugin/reducer.js +19 -19
  120. package/widgets/plugin/styles.js +294 -294
  121. package/widgets/plugin/widget.js +622 -622
  122. package/widgets/quest-run-wizard/service.js +49 -49
  123. package/widgets/quest-run-wizard/ui.js +25 -25
  124. package/widgets/search/styles.js +80 -80
  125. package/widgets/search/widget.js +283 -285
  126. package/widgets/simple/view.js +28 -28
  127. package/widgets/status-filters/widget.js +121 -121
  128. package/widgets/tab/styles.js +16 -16
  129. package/widgets/tab/widget.js +89 -89
  130. package/widgets/tab-content/widget.js +35 -35
  131. package/widgets/tabs/widget.js +48 -48
  132. package/widgets/taskbar/service.js +99 -99
  133. package/widgets/taskbar/view.js +27 -27
  134. package/widgets/taskbar/widget.js +167 -167
  135. package/widgets/widget-doc-caller/reducer.js +15 -15
  136. package/widgets/widget-doc-caller/styles.js +20 -20
  137. package/widgets/widget-doc-caller/widget.js +55 -55
  138. package/widgets/wizard/widget.js +298 -298
  139. package/widgets/wizard-buttons/widget.js +111 -111
  140. package/widgets/workitem/styles.js +286 -286
  141. package/widgets/workitem/view.js +62 -62
  142. package/widgets/workitem/widget.js +988 -988
  143. package/widgets/workitem-dialog/widget.js +86 -86
  144. package/widgets/workitem-fields/widget.js +64 -64
@@ -1,601 +1,601 @@
1
- //T:2019-02-27
2
-
3
- import React from 'react';
4
- import Widget from 'goblin-laboratory/widgets/widget';
5
- import Fragment from 'goblin-gadgets/widgets/fragment/widget.js';
6
- import throttle from 'lodash/throttle';
7
- import CollectionLoader from 'goblin-laboratory/widgets/collection-loader/widget.js';
8
- import List from 'goblin-gadgets/widgets/list/widget';
9
- import TableCell from 'goblin-gadgets/widgets/table-cell/widget';
10
- import TableHeaderDragManager from 'goblin-gadgets/widgets/table-header-drag-manager/widget';
11
- import EntityListItem from 'goblin-desktop/widgets/entity-list-item/widget';
12
- import Shredder from 'xcraft-core-shredder';
13
- import Button from 'goblin-gadgets/widgets/button/widget';
14
- import T from 't';
15
- import MouseTrap from 'mousetrap';
16
- import {Unit} from 'goblin-theme';
17
- import ListHelpers from 'goblin-workshop/lib/list-helpers.js';
18
- import C from 'goblin-laboratory/widgets/connect-helpers/c.js';
19
- import withC from 'goblin-laboratory/widgets/connect-helpers/with-c.js';
20
-
21
- /******************************************************************************/
22
-
23
- class BatchActionsNC extends Widget {
24
- constructor() {
25
- super(...arguments);
26
- }
27
-
28
- onAction = (questService, quest) => {
29
- const {serviceId} = this.props;
30
- this.doFor(serviceId, 'do-batch-action', {questService, questName: quest});
31
- };
32
-
33
- render() {
34
- const {quests, count} = this.props;
35
- return Array.from(quests).map((bq, key) => {
36
- const onClick = () => {
37
- this.onAction(bq.get('questService'), bq.get('quest'));
38
- };
39
- return (
40
- <Button
41
- key={key}
42
- disabled={count === 0}
43
- width="-1px"
44
- height="30px"
45
- glyph={bq.get('glyph')}
46
- tooltip={bq.get('quest')}
47
- onClick={onClick}
48
- text={count}
49
- />
50
- );
51
- });
52
- }
53
- }
54
-
55
- const BatchActions = withC(BatchActionsNC);
56
-
57
- class EntityView extends Widget {
58
- constructor() {
59
- super(...arguments);
60
-
61
- this.state = {
62
- variant: 'bar',
63
- };
64
-
65
- this._entityIds = [];
66
-
67
- this._drillDownInternal = this._drillDownInternal.bind(this);
68
- this._drillDown = throttle(this._drillDownInternal, 100).bind(this);
69
- this.drillDown = this.drillDown.bind(this);
70
- this.selectRow = this.selectRow.bind(this);
71
- this.editRow = this.editRow.bind(this);
72
- this.onEditColumns = this.onEditColumns.bind(this);
73
- this.onSortColumn = this.onSortColumn.bind(this);
74
- this.onWidthChanged = this.onWidthChanged.bind(this);
75
- this.onColumnMoved = this.onColumnMoved.bind(this);
76
- this.onKeyUp = this.onKeyUp.bind(this);
77
- this.onKeyDown = this.onKeyDown.bind(this);
78
- this.onKeyValidate = this.onKeyValidate.bind(this);
79
- this._setDetail = this._setDetail.bind(this);
80
- this.setDetail = throttle(this._setDetail, 1200);
81
- this.renderCollection = this.renderCollection.bind(this);
82
- }
83
-
84
- //#region get/set
85
- get variant() {
86
- return this.state.variant;
87
- }
88
-
89
- set variant(value) {
90
- this.setState({
91
- variant: value,
92
- });
93
- }
94
- //#endregion
95
-
96
- componentDidMount() {
97
- MouseTrap.bind('up', this.onKeyUp, 'keydown');
98
- MouseTrap.bind('down', this.onKeyDown, 'keydown');
99
- MouseTrap.bind('return', this.onKeyValidate);
100
- }
101
-
102
- componentWillUnmount() {
103
- super.componentWillUnmount();
104
- MouseTrap.unbind('up');
105
- MouseTrap.unbind('down');
106
- MouseTrap.unbind('return');
107
- }
108
-
109
- get firstColumnWidth() {
110
- if (this.props.settings) {
111
- const userWidth = this.props.settings.get('widths.id@first-column', null);
112
- if (userWidth) {
113
- return userWidth;
114
- }
115
- }
116
-
117
- return '50px';
118
- }
119
-
120
- getEstimatedWidth(columns) {
121
- let width = ListHelpers.getEstimatedWidth(columns, this.props.settings);
122
-
123
- // Add left and right margins of rows.
124
- width = Unit.add(width, '40px');
125
-
126
- // Add width of first column (and his right margin).
127
- width = Unit.add(width, this.firstColumnWidth);
128
- width = Unit.add(width, '10px');
129
-
130
- // Add right margin after each column.
131
- width = Unit.add(width, Unit.multiply('10px', columns.length));
132
-
133
- return width;
134
- }
135
-
136
- get sorting() {
137
- if (this.props.settings) {
138
- const sorting = this.props.settings.get('sorting', null);
139
- if (sorting) {
140
- return sorting.toJS();
141
- }
142
- }
143
-
144
- return {
145
- columnId: this.props.columnIds.get(0),
146
- direction: 'asc',
147
- };
148
- }
149
-
150
- get filterPaths() {
151
- // TODO...
152
- return ['meta.summaries.description', 'meta.summaries.info'];
153
- }
154
-
155
- getEntityId(rowId) {
156
- const state = new Shredder(this.getState().backend);
157
- return state.get(`list@${this.props.id}.list.${rowId}-item`);
158
- }
159
-
160
- getEntityCount() {
161
- const state = new Shredder(this.getState().backend);
162
- return state.get(`list@${this.props.id}.count`);
163
- }
164
-
165
- getSelectedRowId() {
166
- const state = new Shredder(this.getState().widgets);
167
- return state.get(`${this.props.id}.selectedRowId`);
168
- }
169
-
170
- editRow(rowId, navigate) {
171
- const entityId = this.getEntityId(rowId);
172
- this.doFor(this.props.id, 'open-entity-workitem', {
173
- entityId,
174
- navigate,
175
- });
176
- }
177
-
178
- _setDetail(entityId) {
179
- this.doFor(this.props.id, 'setDetail', {
180
- entityId,
181
- });
182
- }
183
-
184
- selectRow(rowId) {
185
- if (rowId < 0 || rowId >= this.getEntityCount()) {
186
- return;
187
- }
188
- const entityId = this.getEntityId(rowId);
189
- this.dispatch({type: 'select-row', rowId, entityId});
190
- if (entityId && this.props.hinter) {
191
- this.setDetail(entityId);
192
- }
193
- }
194
-
195
- onKeyUp() {
196
- let rowId = parseInt(this.getSelectedRowId());
197
- if (isNaN(rowId)) {
198
- rowId = 1;
199
- }
200
- this.selectRow(rowId - 1);
201
- }
202
-
203
- onKeyDown() {
204
- let rowId = parseInt(this.getSelectedRowId());
205
- if (isNaN(rowId)) {
206
- rowId = -1;
207
- }
208
- this.selectRow(rowId + 1);
209
- }
210
-
211
- onKeyValidate() {
212
- const rowId = parseInt(this.getSelectedRowId());
213
- if (isNaN(rowId)) {
214
- return;
215
- }
216
- this.editRow(rowId);
217
- }
218
-
219
- onEditColumns() {
220
- this.doFor(this.props.id, 'open-entity-workitem', {
221
- entityId: `view@${this.props.type}`,
222
- });
223
- }
224
-
225
- _drillDownInternal() {
226
- let view = null;
227
- if (this.props.view) {
228
- if (this.props.view.toJS) {
229
- view = {view: this.props.view.toJS()};
230
- } else {
231
- view = {view: this.props.view};
232
- }
233
- }
234
- this.doFor(this.props.id, 'drill-down', {
235
- entityIds: this._entityIds,
236
- ...view,
237
- });
238
- this._entityIds = [];
239
- }
240
-
241
- drillDown(entityId) {
242
- this._entityIds.push(entityId);
243
- this._drillDown();
244
- }
245
-
246
- static get wiring() {
247
- return {
248
- id: 'id',
249
- type: 'type',
250
- };
251
- }
252
-
253
- onToggleBatch = () => {
254
- const listId = `list@${this.props.id}`;
255
- this.doFor(listId, 'toggle-batch-select');
256
- };
257
-
258
- onCheckAllBatch = () => {
259
- const listId = `list@${this.props.id}`;
260
- this.doFor(listId, 'check-all-batch');
261
- };
262
-
263
- onUnCheckAllBatch = () => {
264
- const listId = `list@${this.props.id}`;
265
- this.doFor(listId, 'uncheck-all-batch');
266
- };
267
-
268
- onSortColumn(index, columns) {
269
- if (this.props.hasFilter || index === 0) {
270
- return;
271
- }
272
-
273
- const columnId = this.props.columnIds.get(index - 1);
274
- const sorting = this.sorting;
275
- if (sorting.columnId === columnId) {
276
- sorting.direction = sorting.direction === 'asc' ? 'desc' : 'asc';
277
- } else {
278
- sorting.columnId = columnId;
279
- sorting.direction = 'asc';
280
- }
281
-
282
- this.setUserSettings('set-view-column-sorting', {
283
- viewId: `view@${this.props.type}`,
284
- columnId: sorting.columnId,
285
- direction: sorting.direction,
286
- });
287
-
288
- const cell = columns.get(index - 1);
289
- const path = ListHelpers.getColumnPath(cell);
290
-
291
- this.doFor(this.props.id, 'sort-list', {
292
- key: path,
293
- dir: sorting.direction,
294
- });
295
- }
296
-
297
- onWidthChanged(index, width) {
298
- console.log(`onWidthChanged index=${index} width=${width}`);
299
-
300
- if (index === 0) {
301
- this.setUserSettings('set-view-column-width', {
302
- viewId: `view@${this.props.type}`,
303
- columnId: 'id@first-column',
304
- width,
305
- });
306
- } else {
307
- index--;
308
- const columnId = this.props.columnIds.get(index);
309
- this.setUserSettings('set-view-column-width', {
310
- viewId: `view@${this.props.type}`,
311
- columnId,
312
- width,
313
- });
314
- }
315
- }
316
-
317
- onColumnMoved(indexSrc, indexDst) {
318
- console.log(`onColumnMoved indexSrc=${indexSrc} indexDst=${indexDst}`);
319
- indexSrc--;
320
- indexDst--;
321
-
322
- if (indexDst > indexSrc) {
323
- indexDst--; // if moved from left to right, skip the initial column (indexSrc).
324
- }
325
-
326
- const c = this.props.columnIds.valueSeq().toArray();
327
- const srcId = c[indexSrc];
328
- c.splice(indexSrc, 1); // Firstly, remove the column at initial position.
329
- c.splice(indexDst, 0, srcId); // Secondly, insert the column at new position.
330
-
331
- this.setUserSettings('set-view-columns-order', {
332
- viewId: `view@${this.props.type}`,
333
- columnIds: c,
334
- });
335
- }
336
-
337
- /******************************************************************************/
338
-
339
- renderHeaderCell(cell, index) {
340
- let text = ListHelpers.getColumnHeaderText(cell);
341
- const columnId = this.props.columnIds.get(index);
342
- const sorting = this.sorting;
343
- if (sorting.columnId === columnId && !this.props.hasFilter) {
344
- const glyph =
345
- sorting.direction === 'asc' ? 'solid/caret-down' : 'solid/caret-up';
346
- text = new Shredder({text, glyph});
347
- }
348
-
349
- return (
350
- <TableCell
351
- key={index}
352
- isLast={false}
353
- isHeader={true}
354
- verticalAlign="center"
355
- {...ListHelpers.getColumnProps(cell, this.props.settings)}
356
- text={text}
357
- />
358
- );
359
- }
360
-
361
- renderHeader(columns) {
362
- const columnsData = [];
363
- let index = 0;
364
- columnsData.push({index: index++, width: this.firstColumnWidth});
365
- for (const column of columns) {
366
- const props = ListHelpers.getColumnProps(column, this.props.settings);
367
- columnsData.push({index: index++, width: props.width});
368
- }
369
-
370
- return (
371
- <div
372
- className={
373
- this.props.hasFilter
374
- ? this.styles.classNames.headerFilter
375
- : this.styles.classNames.header
376
- }
377
- >
378
- <TableCell
379
- isLast={false}
380
- isHeader={true}
381
- verticalAlign="center"
382
- width={this.firstColumnWidth}
383
- text={T('N°')}
384
- />
385
- {columns.map((c, i) => this.renderHeaderCell(c, i))}
386
- <TableHeaderDragManager
387
- height="44px"
388
- marginLeft="20px"
389
- columns={columnsData}
390
- fixedColumns={[0]}
391
- widthChanged={(index, width) => this.onWidthChanged(index, width)}
392
- columnMoved={(src, dst) => this.onColumnMoved(src, dst)}
393
- columnClicked={(index) => this.onSortColumn(index, columns)}
394
- />
395
- </div>
396
- );
397
- }
398
-
399
- renderRows(columns) {
400
- const listId = `list@${this.props.id}`;
401
-
402
- return (
403
- <div className={this.styles.classNames.rows}>
404
- <List
405
- id={listId}
406
- type={'variable'}
407
- renderItem={EntityListItem}
408
- data={{
409
- firstColumnWidth: this.firstColumnWidth,
410
- filterPaths: this.filterPaths,
411
- hasFilter: this.props.hasFilter,
412
- onDrillDown: this.drillDown,
413
- onRenewTTL: this.renewTTL,
414
- columns: columns,
415
- settings: this.props.settings,
416
- onSelect: this.selectRow,
417
- onEdit: this.editRow,
418
- useView: this.props.view ? true : false,
419
- serviceId: this.props.id,
420
- variant: this.variant,
421
- schema: this.getSchema(this.props.type),
422
- }}
423
- />
424
- </div>
425
- );
426
- }
427
-
428
- renderButton() {
429
- if (!this.props.prototypeMode) {
430
- return null;
431
- }
432
- const listId = `list@${this.props.id}`;
433
- return (
434
- <div className={this.styles.classNames.button}>
435
- <Button
436
- width="30px"
437
- height="30px"
438
- glyph="solid/columns"
439
- tooltip={T('Choisir les colonnes')}
440
- onClick={this.onEditColumns}
441
- />
442
- <Button
443
- width="30px"
444
- height="30px"
445
- glyph={C(`backend.${listId}.enableSelection`, (e) =>
446
- e ? 'solid/times' : 'solid/check'
447
- )}
448
- tooltip={T('Activer / Désactiver la séléction en lots')}
449
- onClick={this.onToggleBatch}
450
- />
451
- <Fragment show={C(`backend.${listId}.enableSelection`)}>
452
- <Button
453
- width="30px"
454
- height="30px"
455
- glyph="solid/check-square"
456
- tooltip={T('Tout cocher')}
457
- onClick={this.onCheckAllBatch}
458
- />
459
- <Button
460
- width="30px"
461
- height="30px"
462
- glyph="regular/square"
463
- tooltip={T('Tout décocher')}
464
- onClick={this.onUnCheckAllBatch}
465
- />
466
- <BatchActions
467
- serviceId={listId}
468
- quests={C(`backend.${listId}.batchQuests`)}
469
- count={C(
470
- `backend.${listId}.selected`,
471
- (s) => Array.from(s.values()).filter((v) => v).length
472
- )}
473
- />
474
- </Fragment>
475
- </div>
476
- );
477
- }
478
-
479
- renderVariants() {
480
- if (!this.props.prototypeMode) {
481
- return null;
482
- }
483
-
484
- return (
485
- <div className={this.styles.classNames.variants}>
486
- <Button
487
- width="28px"
488
- height="20px"
489
- fontSize="75%"
490
- text="J"
491
- tooltip={T('Jauge verticale')}
492
- horizontalSpacing="overlap"
493
- active={this.variant === 'gauge'}
494
- onClick={() => (this.variant = 'gauge')}
495
- />
496
- <Button
497
- width="28px"
498
- height="20px"
499
- fontSize="75%"
500
- text="B"
501
- tooltip={T('Barre verticale')}
502
- horizontalSpacing="overlap"
503
- active={this.variant === 'bar'}
504
- onClick={() => (this.variant = 'bar')}
505
- />
506
- <Button
507
- width="28px"
508
- height="20px"
509
- fontSize="75%"
510
- text="C"
511
- tooltip={T('Carnaval')}
512
- horizontalSpacing="overlap"
513
- active={this.variant === 'carnaval'}
514
- onClick={() => (this.variant = 'carnaval')}
515
- />
516
- </div>
517
- );
518
- }
519
-
520
- renderCollection(columns) {
521
- const width = this.getEstimatedWidth(columns);
522
- const widthStyle = {
523
- minWidth: width,
524
- };
525
-
526
- return (
527
- <div className={this.styles.classNames.entityView}>
528
- <div className={this.styles.classNames.list}>
529
- <div className={this.styles.classNames.content} style={widthStyle}>
530
- {this.renderHeader(columns)}
531
- {this.renderRows(columns)}
532
- </div>
533
- </div>
534
- {this.renderButton(columns)}
535
- {this.renderVariants(columns)}
536
- </div>
537
- );
538
- }
539
-
540
- render() {
541
- const {id, columnIds, isBad, type} = this.props;
542
- if (!id || !columnIds) {
543
- return null;
544
- }
545
-
546
- if (isBad) {
547
- this.setUserSettings('reset-view-column', {
548
- viewId: `view@${type}`,
549
- });
550
- return null;
551
- }
552
-
553
- return (
554
- <CollectionLoader ids={columnIds} returnCollection={true}>
555
- {(collection) => this.renderCollection(collection)}
556
- </CollectionLoader>
557
- );
558
- }
559
- }
560
-
561
- /******************************************************************************/
562
-
563
- const ConnectedEntityView = Widget.connect((state, props) => {
564
- if (!props.type) {
565
- return {};
566
- }
567
-
568
- const hasFilter = !!state.get(`widgets.${props.id}.value`, null);
569
-
570
- const view = state.get(`backend.view@${props.type}`);
571
- let columnIds = view.get('columns');
572
-
573
- const clientSessionId = state.get(`backend.${window.labId}.clientSessionId`);
574
- const userView = state.get(
575
- `backend.${clientSessionId}.views.view@${props.type}`
576
- );
577
- let isBad = false;
578
- if (userView) {
579
- const order = userView.get('order');
580
- if (order.size > 0) {
581
- isBad = order.some((id) => !columnIds.includes(id));
582
- if (!isBad) {
583
- columnIds = order;
584
- }
585
- }
586
- }
587
-
588
- const userSession = Widget.getUserSession(state);
589
- const prototypeMode = userSession.get('prototypeMode');
590
-
591
- return {
592
- isBad,
593
- hasFilter,
594
- columnIds,
595
- view: view.get('query'),
596
- settings: userView,
597
- prototypeMode,
598
- };
599
- })(EntityView);
600
-
601
- export default Widget.Wired(ConnectedEntityView);
1
+ //T:2019-02-27
2
+
3
+ import React from 'react';
4
+ import Widget from 'goblin-laboratory/widgets/widget';
5
+ import Fragment from 'goblin-gadgets/widgets/fragment/widget.js';
6
+ import throttle from 'lodash/throttle';
7
+ import CollectionLoader from 'goblin-laboratory/widgets/collection-loader/widget.js';
8
+ import List from 'goblin-gadgets/widgets/list/widget';
9
+ import TableCell from 'goblin-gadgets/widgets/table-cell/widget';
10
+ import TableHeaderDragManager from 'goblin-gadgets/widgets/table-header-drag-manager/widget';
11
+ import EntityListItem from 'goblin-desktop/widgets/entity-list-item/widget';
12
+ import Shredder from 'xcraft-core-shredder';
13
+ import Button from 'goblin-gadgets/widgets/button/widget';
14
+ import T from 't';
15
+ import MouseTrap from 'mousetrap';
16
+ import {Unit} from 'goblin-theme';
17
+ import ListHelpers from 'goblin-workshop/lib/list-helpers.js';
18
+ import C from 'goblin-laboratory/widgets/connect-helpers/c.js';
19
+ import withC from 'goblin-laboratory/widgets/connect-helpers/with-c.js';
20
+
21
+ /******************************************************************************/
22
+
23
+ class BatchActionsNC extends Widget {
24
+ constructor() {
25
+ super(...arguments);
26
+ }
27
+
28
+ onAction = (questService, quest) => {
29
+ const {serviceId} = this.props;
30
+ this.doFor(serviceId, 'do-batch-action', {questService, questName: quest});
31
+ };
32
+
33
+ render() {
34
+ const {quests, count} = this.props;
35
+ return Array.from(quests).map((bq, key) => {
36
+ const onClick = () => {
37
+ this.onAction(bq.get('questService'), bq.get('quest'));
38
+ };
39
+ return (
40
+ <Button
41
+ key={key}
42
+ disabled={count === 0}
43
+ width="-1px"
44
+ height="30px"
45
+ glyph={bq.get('glyph')}
46
+ tooltip={bq.get('quest')}
47
+ onClick={onClick}
48
+ text={count}
49
+ />
50
+ );
51
+ });
52
+ }
53
+ }
54
+
55
+ const BatchActions = withC(BatchActionsNC);
56
+
57
+ class EntityView extends Widget {
58
+ constructor() {
59
+ super(...arguments);
60
+
61
+ this.state = {
62
+ variant: 'bar',
63
+ };
64
+
65
+ this._entityIds = [];
66
+
67
+ this._drillDownInternal = this._drillDownInternal.bind(this);
68
+ this._drillDown = throttle(this._drillDownInternal, 100).bind(this);
69
+ this.drillDown = this.drillDown.bind(this);
70
+ this.selectRow = this.selectRow.bind(this);
71
+ this.editRow = this.editRow.bind(this);
72
+ this.onEditColumns = this.onEditColumns.bind(this);
73
+ this.onSortColumn = this.onSortColumn.bind(this);
74
+ this.onWidthChanged = this.onWidthChanged.bind(this);
75
+ this.onColumnMoved = this.onColumnMoved.bind(this);
76
+ this.onKeyUp = this.onKeyUp.bind(this);
77
+ this.onKeyDown = this.onKeyDown.bind(this);
78
+ this.onKeyValidate = this.onKeyValidate.bind(this);
79
+ this._setDetail = this._setDetail.bind(this);
80
+ this.setDetail = throttle(this._setDetail, 1200);
81
+ this.renderCollection = this.renderCollection.bind(this);
82
+ }
83
+
84
+ //#region get/set
85
+ get variant() {
86
+ return this.state.variant;
87
+ }
88
+
89
+ set variant(value) {
90
+ this.setState({
91
+ variant: value,
92
+ });
93
+ }
94
+ //#endregion
95
+
96
+ componentDidMount() {
97
+ MouseTrap.bind('up', this.onKeyUp, 'keydown');
98
+ MouseTrap.bind('down', this.onKeyDown, 'keydown');
99
+ MouseTrap.bind('return', this.onKeyValidate);
100
+ }
101
+
102
+ componentWillUnmount() {
103
+ super.componentWillUnmount();
104
+ MouseTrap.unbind('up');
105
+ MouseTrap.unbind('down');
106
+ MouseTrap.unbind('return');
107
+ }
108
+
109
+ get firstColumnWidth() {
110
+ if (this.props.settings) {
111
+ const userWidth = this.props.settings.get('widths.id@first-column', null);
112
+ if (userWidth) {
113
+ return userWidth;
114
+ }
115
+ }
116
+
117
+ return '50px';
118
+ }
119
+
120
+ getEstimatedWidth(columns) {
121
+ let width = ListHelpers.getEstimatedWidth(columns, this.props.settings);
122
+
123
+ // Add left and right margins of rows.
124
+ width = Unit.add(width, '40px');
125
+
126
+ // Add width of first column (and his right margin).
127
+ width = Unit.add(width, this.firstColumnWidth);
128
+ width = Unit.add(width, '10px');
129
+
130
+ // Add right margin after each column.
131
+ width = Unit.add(width, Unit.multiply('10px', columns.length));
132
+
133
+ return width;
134
+ }
135
+
136
+ get sorting() {
137
+ if (this.props.settings) {
138
+ const sorting = this.props.settings.get('sorting', null);
139
+ if (sorting) {
140
+ return sorting.toJS();
141
+ }
142
+ }
143
+
144
+ return {
145
+ columnId: this.props.columnIds.get(0),
146
+ direction: 'asc',
147
+ };
148
+ }
149
+
150
+ get filterPaths() {
151
+ // TODO...
152
+ return ['meta.summaries.description', 'meta.summaries.info'];
153
+ }
154
+
155
+ getEntityId(rowId) {
156
+ const state = new Shredder(this.getState().backend);
157
+ return state.get(`list@${this.props.id}.list.${rowId}-item`);
158
+ }
159
+
160
+ getEntityCount() {
161
+ const state = new Shredder(this.getState().backend);
162
+ return state.get(`list@${this.props.id}.count`);
163
+ }
164
+
165
+ getSelectedRowId() {
166
+ const state = new Shredder(this.getState().widgets);
167
+ return state.get(`${this.props.id}.selectedRowId`);
168
+ }
169
+
170
+ editRow(rowId, navigate) {
171
+ const entityId = this.getEntityId(rowId);
172
+ this.doFor(this.props.id, 'open-entity-workitem', {
173
+ entityId,
174
+ navigate,
175
+ });
176
+ }
177
+
178
+ _setDetail(entityId) {
179
+ this.doFor(this.props.id, 'setDetail', {
180
+ entityId,
181
+ });
182
+ }
183
+
184
+ selectRow(rowId) {
185
+ if (rowId < 0 || rowId >= this.getEntityCount()) {
186
+ return;
187
+ }
188
+ const entityId = this.getEntityId(rowId);
189
+ this.dispatch({type: 'select-row', rowId, entityId});
190
+ if (entityId && this.props.hinter) {
191
+ this.setDetail(entityId);
192
+ }
193
+ }
194
+
195
+ onKeyUp() {
196
+ let rowId = parseInt(this.getSelectedRowId());
197
+ if (isNaN(rowId)) {
198
+ rowId = 1;
199
+ }
200
+ this.selectRow(rowId - 1);
201
+ }
202
+
203
+ onKeyDown() {
204
+ let rowId = parseInt(this.getSelectedRowId());
205
+ if (isNaN(rowId)) {
206
+ rowId = -1;
207
+ }
208
+ this.selectRow(rowId + 1);
209
+ }
210
+
211
+ onKeyValidate() {
212
+ const rowId = parseInt(this.getSelectedRowId());
213
+ if (isNaN(rowId)) {
214
+ return;
215
+ }
216
+ this.editRow(rowId);
217
+ }
218
+
219
+ onEditColumns() {
220
+ this.doFor(this.props.id, 'open-entity-workitem', {
221
+ entityId: `view@${this.props.type}`,
222
+ });
223
+ }
224
+
225
+ _drillDownInternal() {
226
+ let view = null;
227
+ if (this.props.view) {
228
+ if (this.props.view.toJS) {
229
+ view = {view: this.props.view.toJS()};
230
+ } else {
231
+ view = {view: this.props.view};
232
+ }
233
+ }
234
+ this.doFor(this.props.id, 'drill-down', {
235
+ entityIds: this._entityIds,
236
+ ...view,
237
+ });
238
+ this._entityIds = [];
239
+ }
240
+
241
+ drillDown(entityId) {
242
+ this._entityIds.push(entityId);
243
+ this._drillDown();
244
+ }
245
+
246
+ static get wiring() {
247
+ return {
248
+ id: 'id',
249
+ type: 'type',
250
+ };
251
+ }
252
+
253
+ onToggleBatch = () => {
254
+ const listId = `list@${this.props.id}`;
255
+ this.doFor(listId, 'toggle-batch-select');
256
+ };
257
+
258
+ onCheckAllBatch = () => {
259
+ const listId = `list@${this.props.id}`;
260
+ this.doFor(listId, 'check-all-batch');
261
+ };
262
+
263
+ onUnCheckAllBatch = () => {
264
+ const listId = `list@${this.props.id}`;
265
+ this.doFor(listId, 'uncheck-all-batch');
266
+ };
267
+
268
+ onSortColumn(index, columns) {
269
+ if (this.props.hasFilter || index === 0) {
270
+ return;
271
+ }
272
+
273
+ const columnId = this.props.columnIds.get(index - 1);
274
+ const sorting = this.sorting;
275
+ if (sorting.columnId === columnId) {
276
+ sorting.direction = sorting.direction === 'asc' ? 'desc' : 'asc';
277
+ } else {
278
+ sorting.columnId = columnId;
279
+ sorting.direction = 'asc';
280
+ }
281
+
282
+ this.setUserSettings('set-view-column-sorting', {
283
+ viewId: `view@${this.props.type}`,
284
+ columnId: sorting.columnId,
285
+ direction: sorting.direction,
286
+ });
287
+
288
+ const cell = columns.get(index - 1);
289
+ const path = ListHelpers.getColumnPath(cell);
290
+
291
+ this.doFor(this.props.id, 'sort-list', {
292
+ key: path,
293
+ dir: sorting.direction,
294
+ });
295
+ }
296
+
297
+ onWidthChanged(index, width) {
298
+ console.log(`onWidthChanged index=${index} width=${width}`);
299
+
300
+ if (index === 0) {
301
+ this.setUserSettings('set-view-column-width', {
302
+ viewId: `view@${this.props.type}`,
303
+ columnId: 'id@first-column',
304
+ width,
305
+ });
306
+ } else {
307
+ index--;
308
+ const columnId = this.props.columnIds.get(index);
309
+ this.setUserSettings('set-view-column-width', {
310
+ viewId: `view@${this.props.type}`,
311
+ columnId,
312
+ width,
313
+ });
314
+ }
315
+ }
316
+
317
+ onColumnMoved(indexSrc, indexDst) {
318
+ console.log(`onColumnMoved indexSrc=${indexSrc} indexDst=${indexDst}`);
319
+ indexSrc--;
320
+ indexDst--;
321
+
322
+ if (indexDst > indexSrc) {
323
+ indexDst--; // if moved from left to right, skip the initial column (indexSrc).
324
+ }
325
+
326
+ const c = this.props.columnIds.valueSeq().toArray();
327
+ const srcId = c[indexSrc];
328
+ c.splice(indexSrc, 1); // Firstly, remove the column at initial position.
329
+ c.splice(indexDst, 0, srcId); // Secondly, insert the column at new position.
330
+
331
+ this.setUserSettings('set-view-columns-order', {
332
+ viewId: `view@${this.props.type}`,
333
+ columnIds: c,
334
+ });
335
+ }
336
+
337
+ /******************************************************************************/
338
+
339
+ renderHeaderCell(cell, index) {
340
+ let text = ListHelpers.getColumnHeaderText(cell);
341
+ const columnId = this.props.columnIds.get(index);
342
+ const sorting = this.sorting;
343
+ if (sorting.columnId === columnId && !this.props.hasFilter) {
344
+ const glyph =
345
+ sorting.direction === 'asc' ? 'solid/caret-down' : 'solid/caret-up';
346
+ text = new Shredder({text, glyph});
347
+ }
348
+
349
+ return (
350
+ <TableCell
351
+ key={index}
352
+ isLast={false}
353
+ isHeader={true}
354
+ verticalAlign="center"
355
+ {...ListHelpers.getColumnProps(cell, this.props.settings)}
356
+ text={text}
357
+ />
358
+ );
359
+ }
360
+
361
+ renderHeader(columns) {
362
+ const columnsData = [];
363
+ let index = 0;
364
+ columnsData.push({index: index++, width: this.firstColumnWidth});
365
+ for (const column of columns) {
366
+ const props = ListHelpers.getColumnProps(column, this.props.settings);
367
+ columnsData.push({index: index++, width: props.width});
368
+ }
369
+
370
+ return (
371
+ <div
372
+ className={
373
+ this.props.hasFilter
374
+ ? this.styles.classNames.headerFilter
375
+ : this.styles.classNames.header
376
+ }
377
+ >
378
+ <TableCell
379
+ isLast={false}
380
+ isHeader={true}
381
+ verticalAlign="center"
382
+ width={this.firstColumnWidth}
383
+ text={T('N°')}
384
+ />
385
+ {columns.map((c, i) => this.renderHeaderCell(c, i))}
386
+ <TableHeaderDragManager
387
+ height="44px"
388
+ marginLeft="20px"
389
+ columns={columnsData}
390
+ fixedColumns={[0]}
391
+ widthChanged={(index, width) => this.onWidthChanged(index, width)}
392
+ columnMoved={(src, dst) => this.onColumnMoved(src, dst)}
393
+ columnClicked={(index) => this.onSortColumn(index, columns)}
394
+ />
395
+ </div>
396
+ );
397
+ }
398
+
399
+ renderRows(columns) {
400
+ const listId = `list@${this.props.id}`;
401
+
402
+ return (
403
+ <div className={this.styles.classNames.rows}>
404
+ <List
405
+ id={listId}
406
+ type={'variable'}
407
+ renderItem={EntityListItem}
408
+ data={{
409
+ firstColumnWidth: this.firstColumnWidth,
410
+ filterPaths: this.filterPaths,
411
+ hasFilter: this.props.hasFilter,
412
+ onDrillDown: this.drillDown,
413
+ onRenewTTL: this.renewTTL,
414
+ columns: columns,
415
+ settings: this.props.settings,
416
+ onSelect: this.selectRow,
417
+ onEdit: this.editRow,
418
+ useView: this.props.view ? true : false,
419
+ serviceId: this.props.id,
420
+ variant: this.variant,
421
+ schema: this.getSchema(this.props.type),
422
+ }}
423
+ />
424
+ </div>
425
+ );
426
+ }
427
+
428
+ renderButton() {
429
+ if (!this.props.prototypeMode) {
430
+ return null;
431
+ }
432
+ const listId = `list@${this.props.id}`;
433
+ return (
434
+ <div className={this.styles.classNames.button}>
435
+ <Button
436
+ width="30px"
437
+ height="30px"
438
+ glyph="solid/columns"
439
+ tooltip={T('Choisir les colonnes')}
440
+ onClick={this.onEditColumns}
441
+ />
442
+ <Button
443
+ width="30px"
444
+ height="30px"
445
+ glyph={C(`backend.${listId}.enableSelection`, (e) =>
446
+ e ? 'solid/times' : 'solid/check'
447
+ )}
448
+ tooltip={T('Activer / Désactiver la séléction en lots')}
449
+ onClick={this.onToggleBatch}
450
+ />
451
+ <Fragment show={C(`backend.${listId}.enableSelection`)}>
452
+ <Button
453
+ width="30px"
454
+ height="30px"
455
+ glyph="solid/check-square"
456
+ tooltip={T('Tout cocher')}
457
+ onClick={this.onCheckAllBatch}
458
+ />
459
+ <Button
460
+ width="30px"
461
+ height="30px"
462
+ glyph="regular/square"
463
+ tooltip={T('Tout décocher')}
464
+ onClick={this.onUnCheckAllBatch}
465
+ />
466
+ <BatchActions
467
+ serviceId={listId}
468
+ quests={C(`backend.${listId}.batchQuests`)}
469
+ count={C(
470
+ `backend.${listId}.selected`,
471
+ (s) => Array.from(s.values()).filter((v) => v).length
472
+ )}
473
+ />
474
+ </Fragment>
475
+ </div>
476
+ );
477
+ }
478
+
479
+ renderVariants() {
480
+ if (!this.props.prototypeMode) {
481
+ return null;
482
+ }
483
+
484
+ return (
485
+ <div className={this.styles.classNames.variants}>
486
+ <Button
487
+ width="28px"
488
+ height="20px"
489
+ fontSize="75%"
490
+ text="J"
491
+ tooltip={T('Jauge verticale')}
492
+ horizontalSpacing="overlap"
493
+ active={this.variant === 'gauge'}
494
+ onClick={() => (this.variant = 'gauge')}
495
+ />
496
+ <Button
497
+ width="28px"
498
+ height="20px"
499
+ fontSize="75%"
500
+ text="B"
501
+ tooltip={T('Barre verticale')}
502
+ horizontalSpacing="overlap"
503
+ active={this.variant === 'bar'}
504
+ onClick={() => (this.variant = 'bar')}
505
+ />
506
+ <Button
507
+ width="28px"
508
+ height="20px"
509
+ fontSize="75%"
510
+ text="C"
511
+ tooltip={T('Carnaval')}
512
+ horizontalSpacing="overlap"
513
+ active={this.variant === 'carnaval'}
514
+ onClick={() => (this.variant = 'carnaval')}
515
+ />
516
+ </div>
517
+ );
518
+ }
519
+
520
+ renderCollection(columns) {
521
+ const width = this.getEstimatedWidth(columns);
522
+ const widthStyle = {
523
+ minWidth: width,
524
+ };
525
+
526
+ return (
527
+ <div className={this.styles.classNames.entityView}>
528
+ <div className={this.styles.classNames.list}>
529
+ <div className={this.styles.classNames.content} style={widthStyle}>
530
+ {this.renderHeader(columns)}
531
+ {this.renderRows(columns)}
532
+ </div>
533
+ </div>
534
+ {this.renderButton(columns)}
535
+ {this.renderVariants(columns)}
536
+ </div>
537
+ );
538
+ }
539
+
540
+ render() {
541
+ const {id, columnIds, isBad, type} = this.props;
542
+ if (!id || !columnIds) {
543
+ return null;
544
+ }
545
+
546
+ if (isBad) {
547
+ this.setUserSettings('reset-view-column', {
548
+ viewId: `view@${type}`,
549
+ });
550
+ return null;
551
+ }
552
+
553
+ return (
554
+ <CollectionLoader ids={columnIds} returnCollection={true}>
555
+ {(collection) => this.renderCollection(collection)}
556
+ </CollectionLoader>
557
+ );
558
+ }
559
+ }
560
+
561
+ /******************************************************************************/
562
+
563
+ const ConnectedEntityView = Widget.connect((state, props) => {
564
+ if (!props.type) {
565
+ return {};
566
+ }
567
+
568
+ const hasFilter = !!state.get(`widgets.${props.id}.value`, null);
569
+
570
+ const view = state.get(`backend.view@${props.type}`);
571
+ let columnIds = view.get('columns');
572
+
573
+ const clientSessionId = state.get(`backend.${window.labId}.clientSessionId`);
574
+ const userView = state.get(
575
+ `backend.${clientSessionId}.views.view@${props.type}`
576
+ );
577
+ let isBad = false;
578
+ if (userView) {
579
+ const order = userView.get('order');
580
+ if (order.size > 0) {
581
+ isBad = order.some((id) => !columnIds.includes(id));
582
+ if (!isBad) {
583
+ columnIds = order;
584
+ }
585
+ }
586
+ }
587
+
588
+ const userSession = Widget.getUserSession(state);
589
+ const prototypeMode = userSession.get('prototypeMode');
590
+
591
+ return {
592
+ isBad,
593
+ hasFilter,
594
+ columnIds,
595
+ view: view.get('query'),
596
+ settings: userView,
597
+ prototypeMode,
598
+ };
599
+ })(EntityView);
600
+
601
+ export default Widget.Wired(ConnectedEntityView);