@eclipse-scout/core 22.0.0-beta.5 → 22.0.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 (101) hide show
  1. package/dist/eclipse-scout-core-c98fb5230e71dcec75ce.min.js +2 -0
  2. package/dist/eclipse-scout-core-c98fb5230e71dcec75ce.min.js.map +1 -0
  3. package/dist/eclipse-scout-core-theme-6b2fef56e9e49231a49c.min.css +1 -0
  4. package/dist/eclipse-scout-core-theme-dark-d2bb274dd42f132bfca0.min.css +1 -0
  5. package/dist/eclipse-scout-core-theme-dark.css +511 -399
  6. package/dist/eclipse-scout-core-theme-dark.css.map +1 -1
  7. package/dist/eclipse-scout-core-theme.css +247 -135
  8. package/dist/eclipse-scout-core-theme.css.map +1 -1
  9. package/dist/eclipse-scout-core.js +865 -646
  10. package/dist/eclipse-scout-core.js.map +1 -1
  11. package/dist/file-list +7 -0
  12. package/dist/locales.json +47126 -0
  13. package/dist/texts.json +1153 -0
  14. package/package.json +2 -2
  15. package/src/App.js +17 -10
  16. package/src/action/Button.less +1 -0
  17. package/src/box/Box.less +2 -2
  18. package/src/breadcrumbbar/BreadcrumbBarLayout.js +2 -2
  19. package/src/calendar/Calendar.js +40 -58
  20. package/src/calendar/Calendar.less +10 -10
  21. package/src/calendar/CalendarLayout.js +3 -1
  22. package/src/datepicker/DatePicker.less +1 -0
  23. package/src/desktop/desktoptab/DesktopTab.less +19 -2
  24. package/src/desktop/desktoptab/DesktopTabArea.less +7 -3
  25. package/src/desktop/desktoptab/DesktopTabAreaLayout.js +1 -1
  26. package/src/desktop/navigation/DesktopNavigation.less +4 -0
  27. package/src/desktop/notification/DesktopNotification.js +11 -4
  28. package/src/desktop/notification/DesktopNotification.less +5 -3
  29. package/src/desktop/outline/Outline.js +0 -30
  30. package/src/desktop/outline/Outline.less +4 -4
  31. package/src/desktop/viewbutton/ViewButton.less +18 -9
  32. package/src/desktop/viewbutton/ViewButtonBox.js +2 -2
  33. package/src/desktop/viewbutton/ViewMenuTab.less +3 -2
  34. package/src/filechooser/FileChooser.less +1 -1
  35. package/src/form/Form.less +1 -0
  36. package/src/form/fields/LookupBox.js +2 -1
  37. package/src/form/fields/breadcrumbbarfield/BreadcrumbBarField.less +14 -0
  38. package/src/form/fields/groupbox/GroupBox.js +13 -9
  39. package/src/form/fields/groupbox/GroupBox.less +4 -1
  40. package/src/form/fields/htmlfield/HtmlField.less +0 -1
  41. package/src/form/fields/listbox/ListBox.js +8 -3
  42. package/src/form/fields/tabbox/TabAreaLayout.js +63 -66
  43. package/src/form/fields/tabbox/TabBox.js +4 -7
  44. package/src/form/fields/tabbox/TabBox.less +2 -1
  45. package/src/form/fields/tabbox/TabBoxHeaderLayout.js +5 -5
  46. package/src/glasspane/GlassPane.js +3 -3
  47. package/src/group/Group.less +1 -1
  48. package/src/index.js +2 -1
  49. package/src/index.less +1 -0
  50. package/src/jquery/jquery-scout.js +5 -4
  51. package/src/login/LoginBox.less +9 -7
  52. package/src/menu/ContextMenuPopup.less +9 -2
  53. package/src/menu/Menu.less +1 -0
  54. package/src/messagebox/MessageBox.less +3 -3
  55. package/src/modeselector/Mode.less +15 -37
  56. package/src/modeselector/ModeSelector.js +1 -1
  57. package/src/modeselector/ModeSelector.less +2 -1
  58. package/src/planner/PlannerHeader.less +2 -1
  59. package/src/popup/Popup.js +24 -8
  60. package/src/popup/PopupLayout.js +2 -8
  61. package/src/scrollbar/Scrollbar.less +8 -1
  62. package/src/scrollbar/scrollbars.js +26 -4
  63. package/src/session/Session.js +4 -1
  64. package/src/style/colors-dark.less +25 -11
  65. package/src/style/colors.less +17 -3
  66. package/src/style/fonts.less +5 -0
  67. package/src/style/mixins.less +21 -14
  68. package/src/style/sizes-dark.less +4 -1
  69. package/src/style/sizes.less +7 -7
  70. package/src/table/Table.js +45 -33
  71. package/src/table/Table.less +49 -16
  72. package/src/table/TableHeader.js +10 -8
  73. package/src/table/TableHeader.less +1 -0
  74. package/src/table/TableHeaderMenu.js +3 -1
  75. package/src/table/TableHeaderMenu.less +7 -2
  76. package/src/table/columns/BooleanColumn.js +2 -2
  77. package/src/table/columns/Column.js +3 -3
  78. package/src/table/columns/ColumnOptimalWidthMeasurer.js +1 -1
  79. package/src/table/editor/CellEditorPopup.js +8 -1
  80. package/src/tagbar/TagBarOverflowPopupLayout.js +1 -1
  81. package/src/tile/TileGrid.js +1 -1
  82. package/src/tile/TileGridLayout.js +2 -2
  83. package/src/tile/accordion/TileAccordion.js +16 -1
  84. package/src/tile/fields/FormFieldTile.less +18 -11
  85. package/src/tile/fields/button/ButtonTile.js +1 -1
  86. package/src/tile/fields/htmlfield/TileHtmlField.js +28 -0
  87. package/src/tooltip/Tooltip.less +7 -5
  88. package/src/tree/CompactTree.less +1 -1
  89. package/src/tree/LazyNodeFilter.js +26 -15
  90. package/src/tree/Tree.js +114 -143
  91. package/src/tree/Tree.less +3 -5
  92. package/src/tree/TreeLayout.js +1 -1
  93. package/src/tree/TreeNode.js +2 -2
  94. package/src/util/Device.js +6 -2
  95. package/src/util/arrays.js +24 -2
  96. package/src/util/dragAndDrop.js +5 -4
  97. package/src/util/events.js +1 -1
  98. package/src/util/objects.js +4 -1
  99. package/src/widget/FilterSupport.js +7 -5
  100. package/src/widget/FilterSupport.less +38 -9
  101. package/src/widget/Widget.js +24 -7
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
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
@@ -267,7 +267,7 @@ export default class Device {
267
267
  let browsers = Device.Browser;
268
268
  return (browser === browsers.CHROME && version >= 69)
269
269
  || (browser === browsers.FIREFOX && version >= 62)
270
- || (browser === browsers.SAFARI && version >= 12.1);
270
+ || (browser === browsers.SAFARI && version >= 12);
271
271
  }
272
272
 
273
273
  /**
@@ -364,6 +364,10 @@ export default class Device {
364
364
  } else if (this.browser === browsers.EDGE) {
365
365
  versionRegex = /Edge\/([0-9]+\.?[0-9]*)/;
366
366
  } else if (this.browser === browsers.SAFARI) {
367
+ if (this.isIos() && userAgent.indexOf('Version/') < 0) {
368
+ this.browserVersion = this.systemVersion;
369
+ return;
370
+ }
367
371
  versionRegex = /Version\/([0-9]+\.?[0-9]*)/;
368
372
  } else if (this.browser === browsers.FIREFOX) {
369
373
  versionRegex = /Firefox\/([0-9]+\.?[0-9]*)/;
@@ -206,6 +206,11 @@ export function containsAll(haystack, needles) {
206
206
  });
207
207
  }
208
208
 
209
+ /**
210
+ * @template T
211
+ * @param {T[]} arr
212
+ * @return {T}
213
+ */
209
214
  export function first(arr) {
210
215
  if (Array.isArray(arr)) {
211
216
  return arr[0];
@@ -213,6 +218,11 @@ export function first(arr) {
213
218
  return arr;
214
219
  }
215
220
 
221
+ /**
222
+ * @template T
223
+ * @param {T[]} arr
224
+ * @return {T}
225
+ */
216
226
  export function last(arr) {
217
227
  if (Array.isArray(arr)) {
218
228
  return arr[arr.length - 1];
@@ -285,8 +295,8 @@ export function union(array1, array2) {
285
295
  return result;
286
296
  }
287
297
 
288
- // noinspection DuplicatedCode
289
298
  export function equalsIgnoreOrder(arr, arr2) {
299
+ // noinspection DuplicatedCode
290
300
  if (arr === arr2) {
291
301
  return true;
292
302
  }
@@ -302,8 +312,8 @@ export function equalsIgnoreOrder(arr, arr2) {
302
312
  return containsAll(arr, arr2);
303
313
  }
304
314
 
305
- // noinspection DuplicatedCode
306
315
  export function equals(arr, arr2) {
316
+ // noinspection DuplicatedCode
307
317
  if (arr === arr2) {
308
318
  return true;
309
319
  }
@@ -600,6 +610,17 @@ export function toMap(array, keyMapper = (el => el), valueMapper = (el => el)) {
600
610
  }, {}));
601
611
  }
602
612
 
613
+ /**
614
+ * If the argument is an empty array, null is returned. Otherwise, the argument is returned unchanged.
615
+ *
616
+ * @template T
617
+ * @param {T[]} array
618
+ * @return {T[]|null}
619
+ */
620
+ export function nullIfEmpty(array) {
621
+ return empty(array) ? null : array;
622
+ }
623
+
603
624
  export default {
604
625
  $indexOf,
605
626
  $remove,
@@ -638,6 +659,7 @@ export default {
638
659
  max,
639
660
  min,
640
661
  move,
662
+ nullIfEmpty,
641
663
  pushAll,
642
664
  pushIfDefined,
643
665
  pushSet,
@@ -175,13 +175,13 @@ export function _createDragAndDropHandlerOptions(target) {
175
175
  target: target,
176
176
  supportedScoutTypes: dragAndDrop.SCOUT_TYPES.FILE_TRANSFER,
177
177
  validateFiles: (files, defaultValidator) => defaultValidator(files),
178
- onDrop: (files) => {
178
+ onDrop: files => {
179
179
  },
180
180
  dropType: () => dragAndDrop.SCOUT_TYPES.FILE_TRANSFER,
181
181
  dropMaximumSize: () => target.dropMaximumSize,
182
182
  doInstall: () => target.enabledComputed,
183
183
  container: () => target.$container,
184
- additionalDropProperties: (event) => {
184
+ additionalDropProperties: event => {
185
185
  }
186
186
  };
187
187
  }
@@ -269,6 +269,7 @@ export default {
269
269
  * @property {String} [selector] CSS selector which will be added to the event source.
270
270
  * @property {dropType} [dropType] Returns the allowed drop type during a drop event. Default is {@link dragAndDrop.SCOUT_TYPES.FILE_TRANSFER}
271
271
  * @property {dropMaximumSize} [dropMaximumSize] Returns the maximum allowed size of a dropped object. Default is {@link DragAndDropTarget.dropMaximumSize}
272
- * @property {validateFiles} [validateFiles] An optional function to add a custom file validation logic. Throw a {@link dropValidationErrorMessage} to indicate a failed validation. if no custom validator is installed, the default maximum file size validator is invoked.
272
+ * @property {validateFiles} [validateFiles] An optional function to add a custom file validation logic. Throw a {@link dropValidationErrorMessage} to indicate a failed validation.
273
+ * If no custom validator is installed, the default maximum file size validator is invoked.
273
274
  * @property {additionalDropProperties} [additionalDropProperties] Returns additional drop properties to be used in {@link DragAndDropHandler.uploadFiles} as uploadProperties
274
- */
275
+ */
@@ -220,7 +220,7 @@ export function onSwipe($element, id, onDown, onMove, onUp) {
220
220
  let pageX = events.pageX(event);
221
221
  let deltaX = pageX - origPageX;
222
222
  let newLeft = origPosLeft + deltaX;
223
- if (newLeft != curPosLeft) {
223
+ if (newLeft !== curPosLeft) {
224
224
  // only update swipe direction if it actually changed
225
225
  direction = Math.sign(newLeft - curPosLeft);
226
226
  }
@@ -693,11 +693,14 @@ export function resolveConstProperty(object, config) {
693
693
  /**
694
694
  * @param {object} obj
695
695
  * @returns {Boolean|undefined}
696
- * - true if the obj is empty
696
+ * - true if the obj is empty, null or undefined
697
697
  * - false if the obj is not empty
698
698
  * - nothing if the obj is not an object
699
699
  */
700
700
  export function isEmpty(obj) {
701
+ if (isNullOrUndefined(obj)) {
702
+ return true;
703
+ }
701
704
  if (!isPlainObject(obj)) {
702
705
  return;
703
706
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
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
@@ -51,7 +51,7 @@ export default class FilterSupport extends WidgetSupport {
51
51
  scout.assertParameter('getElementsForFiltering', options.getElementsForFiltering);
52
52
  this._getElementsForFiltering = options.getElementsForFiltering;
53
53
  }
54
- this._getElementText = options.getElementText || ((element) => $(element).text());
54
+ this._getElementText = options.getElementText || (element => $(element).text());
55
55
 
56
56
  if (options.createTextFilter) {
57
57
  scout.assertParameter('updateTextFilterText', options.updateTextFilterText);
@@ -126,10 +126,12 @@ export default class FilterSupport extends WidgetSupport {
126
126
  this._filterField.$field.attr('tabIndex', -1);
127
127
 
128
128
  let color = styles.getFirstOpaqueBackgroundColor(this._filterField.$container),
129
- transparentColorRgba = $.extend(true, {}, styles.rgb(color), {alpha: 0.5}),
130
- transparentColor = 'rgba(' + transparentColorRgba.red + ', ' + transparentColorRgba.green + ', ' + transparentColorRgba.blue + ', ' + transparentColorRgba.alpha + ')';
129
+ colorRgba = $.extend(true, {red: 0, green: 0, blue: 0, alpha: 1}, styles.rgb(color)),
130
+ transparent50Color = 'rgba(' + colorRgba.red + ', ' + colorRgba.green + ', ' + colorRgba.blue + ', ' + 0.5 + ')',
131
+ transparent80Color = 'rgba(' + colorRgba.red + ', ' + colorRgba.green + ', ' + colorRgba.blue + ', ' + 0.8 + ')';
131
132
  this._filterField.$container.css('--filter-field-background-color', color);
132
- this._filterField.$container.css('--filter-field-transparent-background-color', transparentColor);
133
+ this._filterField.$container.css('--filter-field-transparent-50-background-color', transparent50Color);
134
+ this._filterField.$container.css('--filter-field-transparent-80-background-color', transparent80Color);
133
135
 
134
136
  this._textFilter = this._createTextFilter();
135
137
  this._textFilter.synthetic = true;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
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
@@ -10,11 +10,12 @@
10
10
  */
11
11
  .filter-field {
12
12
  position: absolute;
13
+ --filter-field-height: @filter-field-height;
13
14
  --filter-field-bottom: @filter-field-bottom;
14
- --filter-field-max-bottom: calc(~'50% - ' @filter-field-height / 2);
15
+ --filter-field-max-bottom: calc(~'50% - ' var(--filter-field-height) / 2);
15
16
  bottom: min(var(--filter-field-bottom), var(--filter-field-max-bottom));
16
17
  right: @filter-field-right;
17
- height: @filter-field-height;
18
+ height: var(--filter-field-height);
18
19
  width: @filter-field-width;
19
20
  min-width: @filter-field-min-width;
20
21
  max-width: @filter-field-max-width;
@@ -25,6 +26,13 @@
25
26
  #scout.drop-shadow();
26
27
  background-color: var(--filter-field-background-color);
27
28
  opacity: 1;
29
+ visibility: visible;
30
+
31
+ // delay the "fade in" transition
32
+ --filter-field-opacity-transition-delay: 100ms;
33
+ // the visibility transition is not a smooth transition but allows to set the value visibility delayed
34
+ // set visibility to visible right before the "fade in" transition starts
35
+ --filter-field-visibility-transition-delay: var(--filter-field-opacity-transition-delay);
28
36
 
29
37
  transition: bottom @filter-field-transition-duration ease-in-out,
30
38
  right @filter-field-transition-duration ease-in-out,
@@ -33,10 +41,17 @@
33
41
  min-width @filter-field-transition-duration ease-in-out,
34
42
  max-width @filter-field-transition-duration ease-in-out,
35
43
  box-shadow @filter-field-transition-duration ease-in-out,
36
- opacity @filter-field-transition-duration ease-in-out;
44
+ opacity @filter-field-transition-duration ease-in-out var(--filter-field-opacity-transition-delay),
45
+ visibility 0s var(--filter-field-visibility-transition-delay);
37
46
 
38
47
  :not(:hover) > &:not(.focused):not(.focus-inside-widget).empty {
39
48
  opacity: 0;
49
+ visibility: hidden;
50
+
51
+ // start the "fade out" transition right away
52
+ --filter-field-opacity-transition-delay: 0s;
53
+ // set visibility to hidden right after the "fade out" transition ends
54
+ --filter-field-visibility-transition-delay: calc(@filter-field-transition-duration + var(--filter-field-opacity-transition-delay));
40
55
  }
41
56
 
42
57
  &::before {
@@ -46,7 +61,7 @@
46
61
  justify-content: center;
47
62
  align-items: center;
48
63
  position: absolute;
49
- bottom: (@filter-field-height - @filter-field-icon-size) / 2;
64
+ bottom: calc((var(--filter-field-height) - @filter-field-icon-size) / 2);
50
65
  right: @text-field-icon-margin-x + 1px;
51
66
  height: @filter-field-icon-size;
52
67
  width: @filter-field-icon-size;
@@ -93,7 +108,7 @@
93
108
  }
94
109
 
95
110
  &:not(.focused).empty {
96
- --filter-field-bottom: @filter-field-bottom + ((@filter-field-height - @filter-field-icon-size) / 2);
111
+ --filter-field-bottom: @filter-field-bottom + ((var(--filter-field-height) - @filter-field-icon-size) / 2);
97
112
  --filter-field-max-bottom: calc(~'50% - ' @filter-field-icon-size / 2);
98
113
  right: @filter-field-right + @text-field-icon-margin-x + 1px;
99
114
  height: @filter-field-icon-size;
@@ -101,7 +116,7 @@
101
116
  min-width: @filter-field-icon-size;
102
117
  max-width: @filter-field-icon-size;
103
118
  box-shadow: none;
104
- #scout.backdrop-filter(@background-color: var(--filter-field-transparent-background-color), @backdrop-filter: blur(2px), @fallback-background-color: var(--filter-field-background-color));
119
+ #scout.backdrop-filter(@background-color: var(--filter-field-transparent-50-background-color), @backdrop-filter: blur(2px), @fallback-background-color: var(--filter-field-transparent-80-background-color));
105
120
 
106
121
  &::before {
107
122
  bottom: 0;
@@ -130,8 +145,9 @@
130
145
 
131
146
  .filter-field-container {
132
147
  position: sticky;
148
+ --filter-field-height: @filter-field-height;
133
149
  --filter-field-container-top: calc(~'100% - ' @filter-field-bottom);
134
- --filter-field-container-min-top: calc(~'50% + ' @filter-field-height / 2);
150
+ --filter-field-container-min-top: calc(~'50% + ' var(--filter-field-height) / 2);
135
151
  top: max(var(--filter-field-container-top), var(--filter-field-container-min-top));
136
152
  left: calc(~'100% - ' @filter-field-right);
137
153
  width: 0;
@@ -143,6 +159,10 @@
143
159
 
144
160
  &:not(:hover) > .filter-field:not(.focused):not(.focus-inside-widget).empty {
145
161
  opacity: 1;
162
+ visibility: visible;
163
+
164
+ --filter-field-opacity-transition-delay: 100ms;
165
+ --filter-field-visibility-transition-delay: var(--filter-field-opacity-transition-delay);
146
166
  }
147
167
 
148
168
  & > .filter-field {
@@ -152,10 +172,14 @@
152
172
 
153
173
  :not(:hover) > &:not(.focused):not(.focus-inside-widget).empty {
154
174
  opacity: 0;
175
+ visibility: hidden;
176
+
177
+ --filter-field-opacity-transition-delay: 0s;
178
+ --filter-field-visibility-transition-delay: calc(@filter-field-transition-duration + var(--filter-field-opacity-transition-delay));
155
179
  }
156
180
 
157
181
  &:not(.focused).empty {
158
- bottom: ((@filter-field-height - @filter-field-icon-size) / 2);
182
+ bottom: calc((var(--filter-field-height) - @filter-field-icon-size) / 2);
159
183
  right: @text-field-icon-margin-x + 1px;
160
184
  }
161
185
  }
@@ -166,4 +190,9 @@
166
190
  .filter-field {
167
191
  .hidden();
168
192
  }
193
+ }
194
+
195
+ .dense .filter-field,
196
+ .dense .filter-field-container {
197
+ --filter-field-height: @filter-field-height-dense;
169
198
  }
@@ -39,7 +39,7 @@ export default class Widget {
39
39
  this.cloneOf = null;
40
40
 
41
41
  /**
42
- * The 'rendering' flag is set the true while the _inital_ rendering is performed.
42
+ * The 'rendering' flag is set the true while the _initial_ rendering is performed.
43
43
  * It is used to to something different in a _render* method when the method is
44
44
  * called for the first time.
45
45
  */
@@ -124,6 +124,12 @@ export default class Widget {
124
124
  READ_ONLY: 1
125
125
  };
126
126
 
127
+ /**
128
+ * Initializes the widget instance. All properties of the model parameter (object) are set as properties on the widget instance.
129
+ * Calls {@link Widget#_init} and triggers an <em>init</em> event when initialization has been completed.
130
+ *
131
+ * @param {object} model
132
+ */
127
133
  init(model) {
128
134
  let staticModel = this._jsonModel();
129
135
  if (staticModel) {
@@ -148,9 +154,12 @@ export default class Widget {
148
154
  }
149
155
 
150
156
  /**
151
- * @param {object} options
152
- * - parent (required): The parent widget
153
- * - session (optional): If not specified the session of the parent is used
157
+ * Initializes the widget instance. All properties of the model parameter (object) are set as properties on the widget instance.
158
+ * Override this function to initialize widget specific properties in sub-classes.
159
+ *
160
+ * @param {object} model Properties:<ul>
161
+ * <li>parent (required): parent widget</li>
162
+ * <li>session (optional): If not specified, session of parent widget is used</li></ul>
154
163
  */
155
164
  _init(model) {
156
165
  if (!model.parent) {
@@ -515,7 +524,8 @@ export default class Widget {
515
524
  * After the animation is executed, the element gets removed using this._removeInternal.
516
525
  */
517
526
  _removeAnimated() {
518
- if (!Device.get().supportsCssAnimation() || !this.$container || this.$container.isDisplayNone()) {
527
+ let animateRemovalWhileRemovingParent = this._animateRemovalWhileRemovingParent();
528
+ if ((this.parent.removing && !animateRemovalWhileRemovingParent) || !Device.get().supportsCssAnimation() || !this.$container || this.$container.isDisplayNone()) {
519
529
  // Cannot remove animated, remove regularly
520
530
  this._removeInternal();
521
531
  return;
@@ -535,7 +545,7 @@ export default class Widget {
535
545
  if (!this.animateRemovalClass) {
536
546
  throw new Error('Missing animate removal class. Cannot remove animated.');
537
547
  }
538
- if (!this.$container.isVisible() || !this.$container.isEveryParentVisible()) {
548
+ if (!this.$container.isVisible() || !this.$container.isEveryParentVisible() || !this.$container.isAttached()) {
539
549
  // If element is not visible, animationEnd would never fire -> remove it immediately
540
550
  this._removeInternal();
541
551
  return;
@@ -548,7 +558,14 @@ export default class Widget {
548
558
 
549
559
  // If the parent is being removed while the animation is running, the animationEnd event will never fire
550
560
  // -> Make sure remove is called nevertheless. Important: remove it before the parent is removed to maintain the regular remove order
551
- this.parent.one('removing', this._parentRemovingWhileAnimatingHandler);
561
+ if (!animateRemovalWhileRemovingParent) {
562
+ this.parent.one('removing', this._parentRemovingWhileAnimatingHandler);
563
+ }
564
+ }
565
+
566
+ _animateRemovalWhileRemovingParent() {
567
+ // By default, remove animation is prevented when parent is being removed
568
+ return false;
552
569
  }
553
570
 
554
571
  _onParentRemovingWhileAnimating() {