@eclipse-scout/core 22.0.33 → 22.0.37
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/dist/eclipse-scout-core-990e036967fa14669bd8.min.js +2 -0
- package/dist/eclipse-scout-core-990e036967fa14669bd8.min.js.map +1 -0
- package/dist/eclipse-scout-core-theme-dark-17a1d77d53510e113a3c.min.css +1 -0
- package/dist/eclipse-scout-core-theme-dark.css +28 -4
- package/dist/eclipse-scout-core-theme-dark.css.map +1 -1
- package/dist/eclipse-scout-core-theme-dfed415a6b7fb17f3f4c.min.css +1 -0
- package/dist/eclipse-scout-core-theme.css +28 -4
- package/dist/eclipse-scout-core-theme.css.map +1 -1
- package/dist/eclipse-scout-core.js +8311 -26955
- package/dist/eclipse-scout-core.js.map +1 -1
- package/dist/file-list +4 -4
- package/dist/texts.json +7 -0
- package/package.json +2 -2
- package/src/App.js +31 -23
- package/src/ErrorHandler.js +3 -2
- package/src/RemoteApp.js +6 -13
- package/src/datepicker/DatePicker.js +5 -9
- package/src/desktop/outline/Outline.js +3 -2
- package/src/form/fields/datefield/DateField.js +26 -8
- package/src/form/fields/datefield/DateFieldAdapter.js +18 -8
- package/src/form/js/JsFormAdapter.js +2 -0
- package/src/keystroke/KeyStroke.js +23 -18
- package/src/lookup/StaticLookupCall.js +28 -3
- package/src/main.less +1 -0
- package/src/popup/Popup.js +94 -15
- package/src/popup/Popup.less +6 -2
- package/src/session/Session.js +10 -5
- package/src/style/sizes.less +2 -0
- package/src/table/TableFooter.js +4 -4
- package/src/table/TableFooter.less +2 -2
- package/src/table/controls/TableControl.js +8 -3
- package/src/table/keystrokes/AbstractTableNavigationKeyStroke.js +1 -1
- package/src/tile/TileGrid.js +21 -9
- package/src/timepicker/TimePicker.js +0 -33
- package/src/tree/Tree.js +46 -5
- package/src/tree/Tree.less +3 -1
- package/src/tree/TreeNode.js +4 -3
- package/dist/eclipse-scout-core-b11dde8e4ac1367f5a06.min.js +0 -2
- package/dist/eclipse-scout-core-b11dde8e4ac1367f5a06.min.js.map +0 -1
- package/dist/eclipse-scout-core-theme-dark-0d9d468e5722f73f5075.min.css +0 -1
- package/dist/eclipse-scout-core-theme-db0af3fa95956b820bfc.min.css +0 -1
package/src/popup/Popup.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* All rights reserved. This program and the accompanying materials
|
|
4
4
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
5
5
|
* which accompanies this distribution, and is available at
|
|
6
|
-
*
|
|
6
|
+
* https://www.eclipse.org/legal/epl-v10.html
|
|
7
7
|
*
|
|
8
8
|
* Contributors:
|
|
9
9
|
* BSI Business Systems Integration AG - initial API and implementation
|
|
@@ -29,6 +29,7 @@ export default class Popup extends Widget {
|
|
|
29
29
|
this.windowPaddingX = 10;
|
|
30
30
|
this.windowPaddingY = 5;
|
|
31
31
|
this.withGlassPane = false;
|
|
32
|
+
this._withGlassPane = null;
|
|
32
33
|
this.withFocusContext = true;
|
|
33
34
|
this.initialFocus = () => FocusRule.AUTO;
|
|
34
35
|
this.focusableContainer = false;
|
|
@@ -67,12 +68,19 @@ export default class Popup extends Widget {
|
|
|
67
68
|
// This is necessary because the mousedown listener is attached to the capture phase and therefore executed before any other.
|
|
68
69
|
// If anchor was clicked, popup would already be closed and then opened again -> popup could never be closed by clicking the anchor
|
|
69
70
|
this.closeOnAnchorMouseDown = true;
|
|
71
|
+
this._closeOnAnchorMouseDown = null;
|
|
70
72
|
|
|
71
73
|
// Defines whether the popup should be closed on a mouse click outside of the popup
|
|
72
74
|
this.closeOnMouseDownOutside = true;
|
|
75
|
+
this._closeOnMouseDownOutside = null;
|
|
73
76
|
|
|
74
77
|
// Defines whether the popup should be closed whenever another popup opens.
|
|
75
78
|
this.closeOnOtherPopupOpen = true;
|
|
79
|
+
this._closeOnOtherPopupOpen = null;
|
|
80
|
+
|
|
81
|
+
// Defines whether the popup should behave like a modal form. If true, the properties closeOnAnchorMouseDown, closeOnMouseDownOutside
|
|
82
|
+
// and closeOnOtherPopupOpen ore overruled and set to false. The property withGlassPane is overruled too and set to true.
|
|
83
|
+
this.modal = false;
|
|
76
84
|
|
|
77
85
|
this._openLater = false;
|
|
78
86
|
|
|
@@ -145,10 +153,8 @@ export default class Popup extends Widget {
|
|
|
145
153
|
if (options.location) {
|
|
146
154
|
this.anchorBounds = new Rectangle(options.location.x, options.location.y, 0, 0);
|
|
147
155
|
}
|
|
148
|
-
if (this.withGlassPane) {
|
|
149
|
-
this._glassPaneRenderer = new GlassPaneRenderer(this);
|
|
150
|
-
}
|
|
151
156
|
this._setAnchor(this.anchor);
|
|
157
|
+
this._setModal(this.modal);
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
/**
|
|
@@ -299,6 +305,7 @@ export default class Popup extends Widget {
|
|
|
299
305
|
this._renderWithArrow();
|
|
300
306
|
this._renderWithFocusContext();
|
|
301
307
|
this._renderWithGlassPane();
|
|
308
|
+
this._renderModal();
|
|
302
309
|
}
|
|
303
310
|
|
|
304
311
|
_postRender() {
|
|
@@ -383,9 +390,89 @@ export default class Popup extends Widget {
|
|
|
383
390
|
this.session.focusManager.installFocusContext(this.$container, FocusRule.PREPARE);
|
|
384
391
|
}
|
|
385
392
|
|
|
393
|
+
setModal(modal) {
|
|
394
|
+
this.setProperty('modal', modal);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
_setModal(modal) {
|
|
398
|
+
this._setProperty('modal', modal);
|
|
399
|
+
if (modal) {
|
|
400
|
+
widgets.preserveAndSetProperty(() => this.setProperty('withGlassPane', true), () => this.withGlassPane, this, '_withGlassPane');
|
|
401
|
+
widgets.preserveAndSetProperty(() => this.setProperty('closeOnAnchorMouseDown', false), () => this.closeOnAnchorMouseDown, this, '_closeOnAnchorMouseDown');
|
|
402
|
+
widgets.preserveAndSetProperty(() => this.setProperty('closeOnMouseDownOutside', false), () => this.closeOnMouseDownOutside, this, '_closeOnMouseDownOutside');
|
|
403
|
+
widgets.preserveAndSetProperty(() => this.setProperty('closeOnOtherPopupOpen', false), () => this.closeOnOtherPopupOpen, this, '_closeOnOtherPopupOpen');
|
|
404
|
+
} else {
|
|
405
|
+
widgets.resetProperty(v => this.setWithGlassPane(v), this, '_withGlassPane');
|
|
406
|
+
widgets.resetProperty(v => this.setCloseOnAnchorMouseDown(v), this, '_closeOnAnchorMouseDown');
|
|
407
|
+
widgets.resetProperty(v => this.setCloseOnMouseDownOutside(v), this, '_closeOnMouseDownOutside');
|
|
408
|
+
widgets.resetProperty(v => this.setCloseOnOtherPopupOpen(v), this, '_closeOnOtherPopupOpen');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
_renderModal() {
|
|
413
|
+
this.$container.toggleClass('modal', this.modal);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
setWithGlassPane(withGlassPane) {
|
|
417
|
+
if (!this.modal) {
|
|
418
|
+
this.setProperty('withGlassPane', withGlassPane);
|
|
419
|
+
} else {
|
|
420
|
+
this._withGlassPane = withGlassPane;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
386
424
|
_renderWithGlassPane() {
|
|
387
|
-
if (this._glassPaneRenderer) {
|
|
425
|
+
if (this.withGlassPane && !this._glassPaneRenderer) {
|
|
426
|
+
this._glassPaneRenderer = new GlassPaneRenderer(this);
|
|
388
427
|
this._glassPaneRenderer.renderGlassPanes();
|
|
428
|
+
} else if (!this.withGlassPane && this._glassPaneRenderer) {
|
|
429
|
+
this._glassPaneRenderer.removeGlassPanes();
|
|
430
|
+
this._glassPaneRenderer = null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
setCloseOnMouseDownOutside(closeOnMouseDownOutside) {
|
|
435
|
+
if (!this.modal) {
|
|
436
|
+
this.setProperty('closeOnMouseDownOutside', closeOnMouseDownOutside);
|
|
437
|
+
} else {
|
|
438
|
+
this._closeOnMouseDownOutside = closeOnMouseDownOutside;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
_renderCloseOnMouseDownOutside() {
|
|
443
|
+
// The listener needs to be executed in the capturing phase -> prevents that _onDocumentMouseDown will be executed right after the popup gets opened using mouse down, otherwise the popup would be closed immediately
|
|
444
|
+
if (this.closeOnMouseDownOutside && !this._documentMouseDownHandler) {
|
|
445
|
+
this._documentMouseDownHandler = this._onDocumentMouseDown.bind(this);
|
|
446
|
+
this.$container.document(true).addEventListener('mousedown', this._documentMouseDownHandler, true); // true=the event handler is executed in the capturing phase
|
|
447
|
+
} else if (!this.closeOnMouseDownOutside && this._documentMouseDownHandler) {
|
|
448
|
+
this.$container.document(true).removeEventListener('mousedown', this._documentMouseDownHandler, true);
|
|
449
|
+
this._documentMouseDownHandler = null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
setCloseOnAnchorMouseDown(closeOnAnchorMouseDown) {
|
|
454
|
+
if (!this.modal) {
|
|
455
|
+
this.setProperty('closeOnAnchorMouseDown', closeOnAnchorMouseDown);
|
|
456
|
+
} else {
|
|
457
|
+
this._closeOnAnchorMouseDown = closeOnAnchorMouseDown;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
setCloseOnOtherPopupOpen(closeOnOtherPopupOpen) {
|
|
462
|
+
if (!this.modal) {
|
|
463
|
+
this.setProperty('closeOnOtherPopupOpen', closeOnOtherPopupOpen);
|
|
464
|
+
} else {
|
|
465
|
+
this._closeOnOtherPopupOpen = closeOnOtherPopupOpen;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
_renderCloseOnOtherPopupOpen() {
|
|
470
|
+
if (this.closeOnOtherPopupOpen && !this._popupOpenHandler) {
|
|
471
|
+
this._popupOpenHandler = this._onPopupOpen.bind(this);
|
|
472
|
+
this.session.desktop.on('popupOpen', this._popupOpenHandler);
|
|
473
|
+
} else if (!this.closeOnOtherPopupOpen && this._popupOpenHandler) {
|
|
474
|
+
this.session.desktop.off('popupOpen', this._popupOpenHandler);
|
|
475
|
+
this._popupOpenHandler = null;
|
|
389
476
|
}
|
|
390
477
|
}
|
|
391
478
|
|
|
@@ -482,17 +569,9 @@ export default class Popup extends Widget {
|
|
|
482
569
|
*/
|
|
483
570
|
_attachCloseHandlers() {
|
|
484
571
|
// Install mouse close handler
|
|
485
|
-
|
|
486
|
-
if (this.closeOnMouseDownOutside) {
|
|
487
|
-
this._documentMouseDownHandler = this._onDocumentMouseDown.bind(this);
|
|
488
|
-
this.$container.document(true).addEventListener('mousedown', this._documentMouseDownHandler, true); // true=the event handler is executed in the capturing phase
|
|
489
|
-
}
|
|
490
|
-
|
|
572
|
+
this._renderCloseOnMouseDownOutside();
|
|
491
573
|
// Install popup open close handler
|
|
492
|
-
|
|
493
|
-
this._popupOpenHandler = this._onPopupOpen.bind(this);
|
|
494
|
-
this.session.desktop.on('popupOpen', this._popupOpenHandler);
|
|
495
|
-
}
|
|
574
|
+
this._renderCloseOnOtherPopupOpen();
|
|
496
575
|
}
|
|
497
576
|
|
|
498
577
|
_attachAnchorHandlers() {
|
package/src/popup/Popup.less
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) 2010-2022 BSI Business Systems Integration AG.
|
|
3
3
|
* All rights reserved. This program and the accompanying materials
|
|
4
4
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
5
5
|
* which accompanies this distribution, and is available at
|
|
6
|
-
*
|
|
6
|
+
* https://www.eclipse.org/legal/epl-v10.html
|
|
7
7
|
*
|
|
8
8
|
* Contributors:
|
|
9
9
|
* BSI Business Systems Integration AG - initial API and implementation
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
&.before-animate-open {
|
|
25
25
|
.invisible();
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
&.modal.animate-modality-highlight {
|
|
29
|
+
#scout.animation-shake();
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
.popup-arrow {
|
package/src/session/Session.js
CHANGED
|
@@ -1069,12 +1069,13 @@ export default class Session {
|
|
|
1069
1069
|
* do nothing. Can be used to prevent double messages for the same error.
|
|
1070
1070
|
*/
|
|
1071
1071
|
showFatalMessage(options, errorCode) {
|
|
1072
|
-
if (errorCode) {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1072
|
+
if (!errorCode) {
|
|
1073
|
+
errorCode = App.get().errorHandler.getJsErrorCode();
|
|
1074
|
+
}
|
|
1075
|
+
if (this._fatalMessagesOnScreen[errorCode]) {
|
|
1076
|
+
return;
|
|
1077
1077
|
}
|
|
1078
|
+
this._fatalMessagesOnScreen[errorCode] = true;
|
|
1078
1079
|
|
|
1079
1080
|
options = options || {};
|
|
1080
1081
|
let model = {
|
|
@@ -1107,6 +1108,10 @@ export default class Session {
|
|
|
1107
1108
|
messageBox.render($entryPoint);
|
|
1108
1109
|
}
|
|
1109
1110
|
|
|
1111
|
+
isFatalMessageShown() {
|
|
1112
|
+
return Object.keys(this._fatalMessagesOnScreen).length > 0;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1110
1115
|
uploadFiles(target, files, uploadProperties, maxTotalSize, allowedTypes) {
|
|
1111
1116
|
let formData = new FormData(),
|
|
1112
1117
|
acceptedFiles = [];
|
package/src/style/sizes.less
CHANGED
|
@@ -351,6 +351,7 @@
|
|
|
351
351
|
@tooltip-padding-x: 12px;
|
|
352
352
|
@tooltip-padding-y: @context-menu-item-padding-y;
|
|
353
353
|
@tree-node-icon-width: 16px;
|
|
354
|
+
@tree-node-icon-padding-right: 9px;
|
|
354
355
|
@tree-node-bitmap-icon-size: @tree-node-icon-width;
|
|
355
356
|
@tree-node-bitmap-icon-margin-top: -2px;
|
|
356
357
|
@tree-node-checkbox-size: 20px;
|
|
@@ -364,6 +365,7 @@
|
|
|
364
365
|
@tree-node-padding-left: 28px;
|
|
365
366
|
@tree-node-padding-right: 7px;
|
|
366
367
|
@tree-node-padding-y: 7px;
|
|
368
|
+
@tree-node-padding-level-diff-parent-has-icon: @tree-node-icon-width + @tree-node-icon-padding-right;
|
|
367
369
|
@view-tab-key-box-bottom: 9px;
|
|
368
370
|
@view-tab-icon-font-size: 20px;
|
|
369
371
|
@view-tab-selected-width: 56px;
|
package/src/table/TableFooter.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2010-
|
|
2
|
+
* Copyright (c) 2010-2022 BSI Business Systems Integration AG.
|
|
3
3
|
* All rights reserved. This program and the accompanying materials
|
|
4
4
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
5
5
|
* which accompanies this distribution, and is available at
|
|
6
|
-
*
|
|
6
|
+
* https://www.eclipse.org/legal/epl-v10.html
|
|
7
7
|
*
|
|
8
8
|
* Contributors:
|
|
9
9
|
* BSI Business Systems Integration AG - initial API and implementation
|
|
@@ -583,13 +583,13 @@ export default class TableFooter extends Widget {
|
|
|
583
583
|
if (footer.animating) {
|
|
584
584
|
// Layout may be called when container stays open but changes its size using an animation.
|
|
585
585
|
// At that time the controlContainer has not yet the final size, therefore measuring is not possible, but not necessary anyway.
|
|
586
|
-
controlContainerHeight = control.height;
|
|
586
|
+
controlContainerHeight = scout.nvl(control && control.height, controlContainerHeight);
|
|
587
587
|
} else {
|
|
588
588
|
// Measure the real height
|
|
589
589
|
controlContainerHeight = graphics.size(footer.$controlContainer).height;
|
|
590
590
|
// Expand control height? (but only if not resizing)
|
|
591
591
|
if (!footer.resizing && growControl) {
|
|
592
|
-
controlContainerHeight = Math.max(control.height, controlContainerHeight);
|
|
592
|
+
controlContainerHeight = Math.max(control && control.height, controlContainerHeight);
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
595
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
left: 0;
|
|
25
25
|
height: 16px;
|
|
26
26
|
cursor: row-resize;
|
|
27
|
-
z-index:
|
|
27
|
+
z-index: 2;
|
|
28
28
|
border-top: @table-control-resize-border-width solid @table-control-resize-border-color;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
display: none;
|
|
47
|
-
z-index:
|
|
47
|
+
z-index: 2; // Must not be smaller than z-index of scrollbar, see Table.less
|
|
48
48
|
/* Reset nowrap, forms may have fields which need wrapping (e.g. label field) */
|
|
49
49
|
white-space: normal;
|
|
50
50
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) 2010-2022 BSI Business Systems Integration AG.
|
|
3
3
|
* All rights reserved. This program and the accompanying materials
|
|
4
4
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
5
5
|
* which accompanies this distribution, and is available at
|
|
6
|
-
*
|
|
6
|
+
* https://www.eclipse.org/legal/epl-v10.html
|
|
7
7
|
*
|
|
8
8
|
* Contributors:
|
|
9
9
|
* BSI Business Systems Integration AG - initial API and implementation
|
|
@@ -99,13 +99,18 @@ export default class TableControl extends Action {
|
|
|
99
99
|
/**
|
|
100
100
|
* Renders the content if not already rendered.<br>
|
|
101
101
|
* Opens the container if the container is not already open.<br>
|
|
102
|
-
* Does nothing if the content is not available yet to -> don't open container if content is not rendered yet to prevent blank container or laggy opening
|
|
102
|
+
* Does nothing if the content is not available yet to -> don't open container if content is not rendered yet to prevent blank container or laggy opening.<br>
|
|
103
|
+
* Does nothing if the control is not selected.
|
|
103
104
|
*/
|
|
104
105
|
renderContent() {
|
|
105
106
|
if (!this.contentRendered && !this.isContentAvailable()) {
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
if (!this.selected) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
109
114
|
if (!this.tableFooter.open) {
|
|
110
115
|
this.tableFooter.openControlContainer(this);
|
|
111
116
|
}
|
package/src/tile/TileGrid.js
CHANGED
|
@@ -51,6 +51,7 @@ export default class TileGrid extends Widget {
|
|
|
51
51
|
this.virtual = false;
|
|
52
52
|
this.virtualScrolling = null;
|
|
53
53
|
this.withPlaceholders = false;
|
|
54
|
+
this.placeholderProducer = null;
|
|
54
55
|
|
|
55
56
|
this.$filterFieldContainer = null;
|
|
56
57
|
this.textFilterEnabled = false;
|
|
@@ -610,6 +611,10 @@ export default class TileGrid extends Widget {
|
|
|
610
611
|
this.invalidateLayoutTree();
|
|
611
612
|
}
|
|
612
613
|
|
|
614
|
+
setPlaceholderProducer(placeholderProducer) {
|
|
615
|
+
this.setProperty('placeholderProducer', placeholderProducer);
|
|
616
|
+
}
|
|
617
|
+
|
|
613
618
|
fillUpWithPlaceholders() {
|
|
614
619
|
if (!this.withPlaceholders) {
|
|
615
620
|
this._deleteAllPlaceholders();
|
|
@@ -623,9 +628,7 @@ export default class TileGrid extends Widget {
|
|
|
623
628
|
if (!this.withPlaceholders) {
|
|
624
629
|
return this.tiles;
|
|
625
630
|
}
|
|
626
|
-
return this.tiles.filter(tile =>
|
|
627
|
-
return !(tile instanceof PlaceholderTile);
|
|
628
|
-
});
|
|
631
|
+
return this.tiles.filter(tile => !(tile instanceof PlaceholderTile));
|
|
629
632
|
}
|
|
630
633
|
|
|
631
634
|
_createPlaceholders() {
|
|
@@ -635,7 +638,8 @@ export default class TileGrid extends Widget {
|
|
|
635
638
|
placeholders = [];
|
|
636
639
|
|
|
637
640
|
if (tiles.length > 0) {
|
|
638
|
-
|
|
641
|
+
let tile = tiles[tiles.length - 1];
|
|
642
|
+
lastX = tile.gridData.x + tile.gridData.w - 1;
|
|
639
643
|
} else {
|
|
640
644
|
// If there are no tiles, create one row with placeholders
|
|
641
645
|
lastX = -1;
|
|
@@ -655,9 +659,17 @@ export default class TileGrid extends Widget {
|
|
|
655
659
|
}
|
|
656
660
|
|
|
657
661
|
_createPlaceholder() {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
662
|
+
let placeholder = (this.placeholderProducer && this.placeholderProducer()) || {};
|
|
663
|
+
if (placeholder instanceof PlaceholderTile) {
|
|
664
|
+
return placeholder;
|
|
665
|
+
}
|
|
666
|
+
if (objects.isPlainObject(placeholder)) {
|
|
667
|
+
return scout.create($.extend(true, {}, {
|
|
668
|
+
objectType: 'PlaceholderTile',
|
|
669
|
+
parent: this
|
|
670
|
+
}, placeholder));
|
|
671
|
+
}
|
|
672
|
+
throw new Error('Placeholder producer returned unexpected result.');
|
|
661
673
|
}
|
|
662
674
|
|
|
663
675
|
_deleteObsoletePlaceholders() {
|
|
@@ -1000,7 +1012,7 @@ export default class TileGrid extends Widget {
|
|
|
1000
1012
|
}
|
|
1001
1013
|
|
|
1002
1014
|
_applyFilters(tiles, fullReset) {
|
|
1003
|
-
return this.filterSupport.applyFilters(tiles, fullReset);
|
|
1015
|
+
return this.filterSupport.applyFilters(tiles.filter(tile => !(tile instanceof PlaceholderTile)), fullReset);
|
|
1004
1016
|
}
|
|
1005
1017
|
|
|
1006
1018
|
/**
|
|
@@ -1010,7 +1022,7 @@ export default class TileGrid extends Widget {
|
|
|
1010
1022
|
return new FilterSupport({
|
|
1011
1023
|
widget: this,
|
|
1012
1024
|
$container: () => this.$filterFieldContainer,
|
|
1013
|
-
getElementsForFiltering: ()
|
|
1025
|
+
getElementsForFiltering: this.tilesWithoutPlaceholders.bind(this),
|
|
1014
1026
|
createTextFilter: this._createTextFilter.bind(this),
|
|
1015
1027
|
updateTextFilterText: this._updateTextFilterText.bind(this)
|
|
1016
1028
|
});
|
|
@@ -204,39 +204,6 @@ export default class TimePicker extends Widget {
|
|
|
204
204
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
_findNextAllowedDate(years, months, days) {
|
|
208
|
-
let i, date,
|
|
209
|
-
sum = years + months + days,
|
|
210
|
-
dir = sum > 0 ? 1 : -1,
|
|
211
|
-
now = this.selectedDate || dates.trunc(new Date());
|
|
212
|
-
|
|
213
|
-
// if we shift by year or month, shift the 'now' date and then use that date as starting point
|
|
214
|
-
// to find the next allowed date.
|
|
215
|
-
if (years !== 0) {
|
|
216
|
-
now = dates.shift(now, years, 0, 0);
|
|
217
|
-
} else if (months !== 0) {
|
|
218
|
-
now = dates.shift(now, 0, months, 0);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (dir === 1) { // find next allowed date, starting from currently selected date
|
|
222
|
-
for (i = 0; i < this.allowedDates.length; i++) {
|
|
223
|
-
date = this.allowedDates[i];
|
|
224
|
-
if (dates.compare(now, date) < 0) {
|
|
225
|
-
return date;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
} else if (dir === -1) { // find previous allowed date, starting from currently selected date
|
|
229
|
-
for (i = this.allowedDates.length - 1; i >= 0; i--) {
|
|
230
|
-
date = this.allowedDates[i];
|
|
231
|
-
if (dates.compare(now, date) > 0) {
|
|
232
|
-
return date;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
207
|
_onNavigationMouseDown(event) {
|
|
241
208
|
let $target = $(event.currentTarget);
|
|
242
209
|
let diff = $target.data('shift');
|
package/src/tree/Tree.js
CHANGED
|
@@ -37,6 +37,7 @@ export default class Tree extends Widget {
|
|
|
37
37
|
this.nodesMap = {}; // all nodes by id
|
|
38
38
|
this.nodePaddingLevelCheckable = 23; /* padding for one tree-level if the tree is checkable */
|
|
39
39
|
this.nodePaddingLevelNotCheckable = 18; /* padding for one tree-level if the tree is not checkable. this includes outline trees! */
|
|
40
|
+
this.nodePaddingLevelDiffParentHasIcon = null; /* is read from CSS */
|
|
40
41
|
this.nodePaddingLeft = null; /* is read from CSS */
|
|
41
42
|
this.nodeCheckBoxPaddingLeft = 29;
|
|
42
43
|
this.nodeControlPaddingLeft = null; /* is read from CSS */
|
|
@@ -70,6 +71,7 @@ export default class Tree extends Widget {
|
|
|
70
71
|
|
|
71
72
|
// contains all parents of a selected node, the selected node and the first level children
|
|
72
73
|
this._inSelectionPathList = {};
|
|
74
|
+
this._changeNodeTaskScheduled = false;
|
|
73
75
|
this.viewRangeRendered = new Range(0, 0);
|
|
74
76
|
this.viewRangeSize = 20;
|
|
75
77
|
|
|
@@ -890,7 +892,7 @@ export default class Tree extends Widget {
|
|
|
890
892
|
|
|
891
893
|
_renderNode(node) {
|
|
892
894
|
let paddingLeft = this._computeNodePaddingLeft(node);
|
|
893
|
-
node.render(this.$container, paddingLeft
|
|
895
|
+
node.render(this.$container, paddingLeft);
|
|
894
896
|
return node.$node;
|
|
895
897
|
}
|
|
896
898
|
|
|
@@ -953,7 +955,7 @@ export default class Tree extends Widget {
|
|
|
953
955
|
let $control = $node.children('.tree-node-control');
|
|
954
956
|
let $checkbox = $node.children('.tree-node-checkbox');
|
|
955
957
|
|
|
956
|
-
node._updateControl($control
|
|
958
|
+
node._updateControl($control);
|
|
957
959
|
if (this.checkable) {
|
|
958
960
|
if ($checkbox.length === 0) {
|
|
959
961
|
node._renderCheckbox();
|
|
@@ -1312,7 +1314,7 @@ export default class Tree extends Widget {
|
|
|
1312
1314
|
let node = $node.data('node');
|
|
1313
1315
|
let paddingLeft = this._computeNodePaddingLeft(node);
|
|
1314
1316
|
$node.cssPaddingLeft(objects.isNullOrUndefined(paddingLeft) ? '' : paddingLeft);
|
|
1315
|
-
node._updateControl($node.children('.tree-node-control')
|
|
1317
|
+
node._updateControl($node.children('.tree-node-control'));
|
|
1316
1318
|
});
|
|
1317
1319
|
}
|
|
1318
1320
|
|
|
@@ -2087,18 +2089,41 @@ export default class Tree extends Widget {
|
|
|
2087
2089
|
if (this.isBreadcrumbStyleActive()) {
|
|
2088
2090
|
return this.nodePaddingLeft;
|
|
2089
2091
|
}
|
|
2090
|
-
let padding =
|
|
2092
|
+
let padding = this.nodePaddingLeft + this._computeNodePaddingLeftForLevel(node);
|
|
2091
2093
|
if (this.checkable) {
|
|
2092
2094
|
padding += this.nodeCheckBoxPaddingLeft;
|
|
2093
2095
|
}
|
|
2094
2096
|
return padding;
|
|
2095
2097
|
}
|
|
2096
2098
|
|
|
2099
|
+
_computeNodeControlPaddingLeft(node) {
|
|
2100
|
+
return this.nodeControlPaddingLeft + this._computeNodePaddingLeftForLevel(node);
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
_computeNodePaddingLeftForLevel(node) {
|
|
2104
|
+
if (this.checkable || !this.nodePaddingLevelDiffParentHasIcon) {
|
|
2105
|
+
return node.level * this.nodePaddingLevel;
|
|
2106
|
+
}
|
|
2107
|
+
let padding = 0;
|
|
2108
|
+
let parentNode = node.parentNode;
|
|
2109
|
+
while (parentNode) {
|
|
2110
|
+
padding += this.nodePaddingLevel;
|
|
2111
|
+
// Increase the padding if the parent node has an icon to make the hierarchy more clear
|
|
2112
|
+
// This is not necessary if the child nodes have icons as well, the padding even looks too big, as it is the case for checkable trees.
|
|
2113
|
+
// We only check the first child node for an icon because that one has the biggest impact on the hierarchy visualization. It also increases performance a little.
|
|
2114
|
+
if (parentNode.iconId && !parentNode.childNodes[0].iconId) {
|
|
2115
|
+
padding += this.nodePaddingLevelDiffParentHasIcon;
|
|
2116
|
+
}
|
|
2117
|
+
parentNode = parentNode.parentNode;
|
|
2118
|
+
}
|
|
2119
|
+
return padding;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2097
2122
|
/**
|
|
2098
2123
|
* Reads the paddings from CSS and stores them in nodePaddingLeft and nodeControlPaddingLeft
|
|
2099
2124
|
*/
|
|
2100
2125
|
_computeNodePaddings() {
|
|
2101
|
-
if (this.nodePaddingLeft !== null && this.nodeControlPaddingLeft !== null) {
|
|
2126
|
+
if (this.nodePaddingLeft !== null && this.nodeControlPaddingLeft !== null && this.nodePaddingLevelDiffParentHasIcon !== null) {
|
|
2102
2127
|
return;
|
|
2103
2128
|
}
|
|
2104
2129
|
let $dummyNode = this.$data.appendDiv('tree-node');
|
|
@@ -2109,6 +2134,9 @@ export default class Tree extends Widget {
|
|
|
2109
2134
|
if (this.nodeControlPaddingLeft === null) {
|
|
2110
2135
|
this.nodeControlPaddingLeft = $dummyNodeControl.cssPaddingLeft();
|
|
2111
2136
|
}
|
|
2137
|
+
if (this.nodePaddingLevelDiffParentHasIcon === null) {
|
|
2138
|
+
this.nodePaddingLevelDiffParentHasIcon = this.$container.cssPxValue('--node-padding-level-diff-parent-has-icon');
|
|
2139
|
+
}
|
|
2112
2140
|
$dummyNode.remove();
|
|
2113
2141
|
}
|
|
2114
2142
|
|
|
@@ -3169,6 +3197,19 @@ export default class Tree extends Widget {
|
|
|
3169
3197
|
this.applyFiltersForNode(node);
|
|
3170
3198
|
if (this.rendered) {
|
|
3171
3199
|
node._decorate();
|
|
3200
|
+
// The padding size of a node depends on whether the node or the parent node has an icon, see _computeNodePaddingLeftForLevel
|
|
3201
|
+
// Unfortunately, we cannot easily detect whether the icon has changed or not.
|
|
3202
|
+
// However, the padding calculation only needs to be done if the node that toggles the icon is visible and expanded or has an expanded parent.
|
|
3203
|
+
let paddingDirty = !!this.nodePaddingLevelDiffParentHasIcon && !!this.visibleNodesMap[node.id] && (node.expanded || !!node.parentNode);
|
|
3204
|
+
if (paddingDirty && !this._changeNodeTaskScheduled) {
|
|
3205
|
+
// Because the change node event is not batch capable, performance would slow down if many change node events are processed
|
|
3206
|
+
// To mitigate this, the updating is done later
|
|
3207
|
+
queueMicrotask(() => {
|
|
3208
|
+
this._updateNodePaddingsLeft();
|
|
3209
|
+
this._changeNodeTaskScheduled = false;
|
|
3210
|
+
});
|
|
3211
|
+
this._changeNodeTaskScheduled = true;
|
|
3212
|
+
}
|
|
3172
3213
|
}
|
|
3173
3214
|
this.trigger('nodeChanged', {
|
|
3174
3215
|
node: node
|
package/src/tree/Tree.less
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
.tree {
|
|
12
12
|
position: relative;
|
|
13
|
+
// The value of the css variable is read by Tree.js and added to the level padding if the parent node has an icon
|
|
14
|
+
--node-padding-level-diff-parent-has-icon: @tree-node-padding-level-diff-parent-has-icon;
|
|
13
15
|
|
|
14
16
|
&:focus,
|
|
15
17
|
&.focused {
|
|
@@ -122,7 +124,7 @@
|
|
|
122
124
|
|
|
123
125
|
& > .icon {
|
|
124
126
|
vertical-align: top;
|
|
125
|
-
padding-right:
|
|
127
|
+
padding-right: @tree-node-icon-padding-right;
|
|
126
128
|
display: inline-block;
|
|
127
129
|
text-align: center;
|
|
128
130
|
min-width: @tree-node-icon-width;
|
package/src/tree/TreeNode.js
CHANGED
|
@@ -253,12 +253,13 @@ export default class TreeNode {
|
|
|
253
253
|
|
|
254
254
|
_renderControl() {
|
|
255
255
|
let $control = this.$node.prependDiv('tree-node-control');
|
|
256
|
-
this._updateControl($control
|
|
256
|
+
this._updateControl($control);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
_updateControl($control
|
|
259
|
+
_updateControl($control) {
|
|
260
|
+
let tree = this.getTree();
|
|
260
261
|
$control.toggleClass('checkable', tree.checkable);
|
|
261
|
-
$control.cssPaddingLeft(tree.
|
|
262
|
+
$control.cssPaddingLeft(tree._computeNodeControlPaddingLeft(this));
|
|
262
263
|
$control.setVisible(!this.leaf);
|
|
263
264
|
}
|
|
264
265
|
|