bm-core-ui 2.11.6 → 2.11.8

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.
@@ -8216,6 +8216,12 @@ export class BMMenu {
8216
8216
  */
8217
8217
  private _node?: DOMNode | null | undefined;
8218
8218
 
8219
+ /**
8220
+ * The DOM node containing the menu items.
8221
+ *
8222
+ */
8223
+ private _itemsContainerNode?: DOMNode | null | undefined;
8224
+
8219
8225
  /**
8220
8226
  * The container DOM node currently used by this menu
8221
8227
  *
@@ -12834,7 +12840,7 @@ export interface BMCollectionViewDataSet<T = any> {
12834
12840
  * @return An array of index paths specifying the positions of the items after they have been moved.
12835
12841
  * The index paths in this array are not required to match either of the lists supplied by
12836
12842
  * collection view.
12837
- *
12843
+ *
12838
12844
  */
12839
12845
  moveItemsFromIndexPaths?(indexPaths: BMIndexPath<T>[], {toIndexPath}: {toIndexPath: BMIndexPath<T>}): BMIndexPath<T>[];
12840
12846
 
@@ -12847,7 +12853,7 @@ export interface BMCollectionViewDataSet<T = any> {
12847
12853
  * and remove just some of the items, by changing their internal data structures appropriately.
12848
12854
  * The order of the items in the array is guaranteed to be such that the target index paths are in ascending order.
12849
12855
  * @param indexPaths An array of index paths identifying which items have to be removed.
12850
- *
12856
+ *
12851
12857
  */
12852
12858
  removeItemsAtIndexPaths?(indexPaths: BMIndexPath<T>[]): void;
12853
12859
 
@@ -12860,10 +12866,23 @@ export interface BMCollectionViewDataSet<T = any> {
12860
12866
  * and add just some of the items, by changing their internal data structures appropriately.
12861
12867
  * @param items An array of objects to add to the collection view.
12862
12868
  * @param toIndexPath The starting index path to which the items should be inserted.
12863
- *
12869
+ *
12864
12870
  */
12865
12871
  insertItems?(items: any[], {toIndexPath}: {toIndexPath: BMIndexPath<T>}): void;
12866
12872
 
12873
+
12874
+ /**
12875
+ * This method may be implemented by data set objects that support transferring items to another collection view.
12876
+ * Data set objects implementing this method are expected to create a copy of the specified item and return it.
12877
+ * The item is guaranteed to be an object that was returned at some point by the data set when it provided an index path.
12878
+ * If this method is not implemented, collection view will create a copy of the item by stringifying it and parsing it back
12879
+ * into an object.
12880
+ * @param item The item whose copy should be created for the transfer.
12881
+ * @return A copy of the specified item.
12882
+ *
12883
+ */
12884
+ copyOfItem?(item: any): any;
12885
+
12867
12886
  }
12868
12887
 
12869
12888
 
@@ -13043,6 +13062,20 @@ export interface BMCollectionViewDataSource<T = any> {
13043
13062
  */
13044
13063
  collectionViewInsertItems?(collectionView: BMCollectionView, items: any[], {toIndexPath}: {toIndexPath: BMIndexPath<T>}): void;
13045
13064
 
13065
+
13066
+ /**
13067
+ * This method may be implemented by data source objects that support transferring items to another collection view.
13068
+ * Data source objects implementing this method are expected to create a copy of the specified item and return it.
13069
+ * The item is guaranteed to be an object that was returned at some point by the data set when it provided an index path.
13070
+ * If this method is not implemented, collection view will create a copy of the item by stringifying it and parsing it back
13071
+ * into an object.
13072
+ * @param collectionView The collection view from which items are being transferred.
13073
+ * @param item The item whose copy should be created for the transfer.
13074
+ * @return A copy of the specified item.
13075
+ *
13076
+ */
13077
+ collectionViewCopyOfItem?(collectionView: BMCollectionView, item: any): any;
13078
+
13046
13079
  }
13047
13080
 
13048
13081
 
@@ -13831,7 +13864,7 @@ export class BMWindow extends BMView {
13831
13864
  * The DOM node in which the window contents should be added.
13832
13865
  *
13833
13866
  */
13834
- readonly content: DOMNode;
13867
+ private _content: DOMNode;
13835
13868
 
13836
13869
  /**
13837
13870
  * A string that is used for when this window is minimized.
@@ -13991,6 +14024,12 @@ export class BMWindow extends BMView {
13991
14024
  toggleAnimated(animated?: boolean | null | undefined, {completionHandler}?: {completionHandler?: (() => void) | null | undefined}): void;
13992
14025
 
13993
14026
 
14027
+ /**
14028
+ * When set to `YES`, the anchor node remains hidden while the window is visible.
14029
+ *
14030
+ */
14031
+ private _keepsNodeHidden: boolean;
14032
+
13994
14033
  /**
13995
14034
  * This method is invoked during `bringToFrontAnimated()` to run the animations that will make this window visible.
13996
14035
  * Subclasses can override this method to perform custom animations.
@@ -3580,17 +3580,22 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3580
3580
  // The list of objects will be a copy of the actual objects
3581
3581
  let dropShadowMap = new Map;
3582
3582
  let dropItems = this._draggingIndexPaths.map(indexPath => {
3583
- const newObject = JSON.parse(JSON.stringify(indexPath.object));
3584
-
3585
- // stringify + parse strips out undefined values, but this can lead to issues
3586
- // in Thingworx when testing the new object against the data shape
3587
- for (const key in indexPath.object) {
3588
- if (typeof indexPath.object[key] === 'undefined') {
3589
- newObject[key] = undefined;
3590
- }
3583
+ if (this._dataSet.copyOfItem) {
3584
+ return this._dataSet.copyOfItem(indexPath.object);
3591
3585
  }
3586
+ else {
3587
+ const newObject = JSON.parse(JSON.stringify(indexPath.object));
3592
3588
 
3593
- return newObject;
3589
+ // stringify + parse strips out undefined values, but this can lead to issues
3590
+ // in Thingworx when testing the new object against the data shape
3591
+ for (const key in indexPath.object) {
3592
+ if (typeof indexPath.object[key] === 'undefined') {
3593
+ newObject[key] = undefined;
3594
+ }
3595
+ }
3596
+
3597
+ return newObject;
3598
+ }
3594
3599
  });
3595
3600
  let index = 0;
3596
3601
 
@@ -6423,7 +6428,15 @@ class BMCollectionViewDataSourceAdapter {
6423
6428
  }
6424
6429
 
6425
6430
  static adapterWithDataSet(dataSet) {
6426
- return new this(dataSet);
6431
+ const adapter = new this(dataSet);
6432
+
6433
+ if (dataSet.copyOfItem) {
6434
+ adapter.collectionViewCopyOfItem = function (collectionView, item) {
6435
+ return dataSet.copyOfItem(item);
6436
+ };
6437
+ }
6438
+
6439
+ return adapter;
6427
6440
  }
6428
6441
  }
6429
6442
 
@@ -6452,7 +6465,7 @@ class BMCollectionViewDataSetAdapter {
6452
6465
  }
6453
6466
 
6454
6467
  indexPathForObjectAtRow(row, args) {
6455
- return this._dataSource.collectionViewNumberOfObjectsInSectionAtIndex(this._collectionView, row, args);
6468
+ return this._dataSource.collectionViewIndexPathForObjectAtRow(this._collectionView, row, args);
6456
6469
  }
6457
6470
 
6458
6471
  indexPathForObject(object) {
@@ -6508,7 +6521,15 @@ class BMCollectionViewDataSetAdapter {
6508
6521
  }
6509
6522
 
6510
6523
  static adapterWithDataSource(collectionView, dataSource) {
6511
- return new this(collectionView, dataSource);
6524
+ const adapter = new this(collectionView, dataSource);
6525
+
6526
+ if (dataSource.collectionViewCopyOfItem) {
6527
+ adapter.copyOfItem = function (item) {
6528
+ return dataSource.collectionViewCopyOfItem(adapter._collectionView, item);
6529
+ };
6530
+ }
6531
+
6532
+ return adapter;
6512
6533
  }
6513
6534
  }
6514
6535
 
@@ -95,6 +95,10 @@
95
95
 
96
96
  /* MARK: BMWindow */
97
97
 
98
+ .BMWindowAnchorNodeHidden {
99
+ visibility: hidden !important;
100
+ }
101
+
98
102
  .BMWindowShowcaseWindow, .BMWindowShowcaseWindow *:not(.BMWindowShowcaseWindowSelector) {
99
103
  pointer-events: none !important;
100
104
  }
@@ -1157,12 +1161,6 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1157
1161
  .BMMenu {
1158
1162
  position: absolute;
1159
1163
 
1160
- display: flex;
1161
- flex-direction: column;
1162
-
1163
- padding-top: 4px;
1164
- padding-bottom: 4px;
1165
-
1166
1164
  background-color: rgba(255, 255, 255, .8);
1167
1165
  box-shadow: 0px 4px 8px rgba(0, 0, 0, .1), 0px 0px 1px 1px rgba(0, 0, 0, .1);
1168
1166
 
@@ -1173,7 +1171,23 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1173
1171
  backdrop-filter: blur(25px);
1174
1172
  -webkit-backdrop-filter: blur(25px);
1175
1173
 
1174
+ overflow: hidden;
1175
+
1176
+ display: flex;
1177
+ flex-direction: column;
1178
+ }
1179
+
1180
+ .BMMenu > .BMMenuItemsContainer {
1181
+ display: flex;
1182
+ flex-direction: column;
1183
+
1184
+ padding-top: 4px;
1185
+ padding-bottom: 4px;
1186
+
1176
1187
  overflow-y: auto;
1188
+
1189
+ width: 100%;
1190
+ height: 100%;
1177
1191
  }
1178
1192
 
1179
1193
  .BMLayoutEditorConstraintPopup::before, .BMMenu::before {
@@ -1218,7 +1232,7 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1218
1232
  }
1219
1233
 
1220
1234
  /* While a touch menu has a submenu, its menu items should not respond to events */
1221
- .BMMenuTouchSubmenu.BMMenu > .BMMenuItem {
1235
+ .BMMenuTouchSubmenu.BMMenu > .BMMenuItemsContainer > .BMMenuItem {
1222
1236
  pointer-events: none;
1223
1237
 
1224
1238
  /* Additionally add an opacity transition to the items to be used in an animation */
@@ -1302,21 +1316,21 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1302
1316
  padding-right: 64px;
1303
1317
  }
1304
1318
 
1305
- .BMMenuTouch > .BMMenuItem {
1319
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem {
1306
1320
  height: 44px;
1307
1321
  line-height: 44px;
1308
1322
  border-top: 1px solid rgba(0, 0, 0, .1);
1309
1323
  }
1310
1324
 
1311
- .BMMenuTouch > .BMMenuItem:first-child {
1325
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem:first-child {
1312
1326
  border-top-width: 0px;
1313
1327
  }
1314
1328
 
1315
- .BMMenuTouch > .BMMenuItem.BMMenuItemHighlighted + .BMMenuItem {
1329
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem.BMMenuItemHighlighted + .BMMenuItem {
1316
1330
  border-top-color: rgba(0, 0, 0, .2);
1317
1331
  }
1318
1332
 
1319
- .BMMenuTouch > .BMMenuItem.BMMenuItemHighlighted + .BMMenuDivider {
1333
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem.BMMenuItemHighlighted + .BMMenuDivider {
1320
1334
  box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, .1) inset;
1321
1335
  }
1322
1336
 
@@ -1395,7 +1409,7 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1395
1409
  }
1396
1410
  }
1397
1411
 
1398
- .BMLayoutEditorConstraintPopupOption:hover, .BMMenuTouch > .BMMenuItem:not(.BMMenuItemDisabled):active, .BMMenuItem.BMMenuItemHighlighted {
1412
+ .BMLayoutEditorConstraintPopupOption:hover, .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem:not(.BMMenuItemDisabled):active, .BMMenuItem.BMMenuItemHighlighted {
1399
1413
  background: rgb(0, 128, 255);
1400
1414
  color: white;
1401
1415
  }
@@ -1405,7 +1419,7 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1405
1419
  color: rgba(0, 0, 0, .8);
1406
1420
  }
1407
1421
 
1408
- .BMLayoutEditorConstraintPopupOption:active, .BMMenuTouch > .BMMenuItem:not(.BMMenuItemDisabled):hover {
1422
+ .BMLayoutEditorConstraintPopupOption:active, .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem:not(.BMMenuItemDisabled):hover {
1409
1423
  background: transparent;
1410
1424
  color: rgba(0, 0, 0, .8);
1411
1425
  }
@@ -1415,8 +1429,8 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1415
1429
  animation-fill-mode: both;
1416
1430
  }
1417
1431
 
1418
- .BMMenuTouch > .BMMenuItem.BMMenuItemActive,
1419
- .BMMenuTouch > .BMMenuItem.BMMenuItemHighlighted {
1432
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem.BMMenuItemActive,
1433
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem.BMMenuItemHighlighted {
1420
1434
  animation: none;
1421
1435
  background: transparent;
1422
1436
  color: rgba(0, 0, 0, .8);
@@ -1438,17 +1452,17 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1438
1452
  color: rgba(255, 255, 255, .45);
1439
1453
  }
1440
1454
 
1441
- .BMLayoutEditorConstraintPopupOption:active, .BMMenuTouch > .BMMenuItem:not(.BMMenuItemDisabled):hover {
1455
+ .BMLayoutEditorConstraintPopupOption:active, .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem:not(.BMMenuItemDisabled):hover {
1442
1456
  background: transparent;
1443
1457
  color: white;
1444
1458
  }
1445
1459
 
1446
- .BMMenuTouch > .BMMenuItem {
1460
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem {
1447
1461
  border-bottom-color: rgba(255, 255, 255, .1);
1448
1462
  }
1449
1463
 
1450
- .BMMenuTouch > .BMMenuItem.BMMenuItemActive,
1451
- .BMMenuTouch > .BMMenuItem.BMMenuItemHighlighted {
1464
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem.BMMenuItemActive,
1465
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuItem.BMMenuItemHighlighted {
1452
1466
  color: rgba(255, 255, 255, 1);
1453
1467
 
1454
1468
  backdrop-filter: brightness(110%);
@@ -1613,7 +1627,7 @@ input:not(:checked) + .BMWindowSwitchGutter > .BMWindowSwitchKnob, .BMWindowSwit
1613
1627
  flex-shrink: 0;
1614
1628
  }
1615
1629
 
1616
- .BMMenuTouch > .BMMenuDivider {
1630
+ .BMMenuTouch > .BMMenuItemsContainer > .BMMenuDivider {
1617
1631
  margin: 0;
1618
1632
  height: 3px;
1619
1633
  background: rgba(0, 0, 0, .1);
@@ -346,6 +346,11 @@ BMMenu.prototype = {
346
346
  */
347
347
  _node: undefined, // <DOMNode, nullable>
348
348
 
349
+ /**
350
+ * The DOM node containing the menu items.
351
+ */
352
+ _itemsContainerNode: undefined, // <DOMNode, nullable>
353
+
349
354
  /**
350
355
  * The container DOM node currently used by this menu
351
356
  */
@@ -413,9 +418,15 @@ BMMenu.prototype = {
413
418
  }
414
419
 
415
420
  // And add it to the newly highlighted item
416
- const nextHighlightedItem = this._node.children[index];
421
+ const nextHighlightedItem = this._itemsContainerNode.children[index];
417
422
  if (nextHighlightedItem) {
418
423
  nextHighlightedItem.classList.add('BMMenuItemHighlighted');
424
+ if (nextHighlightedItem.scrollIntoViewIfNeeded) {
425
+ nextHighlightedItem.scrollIntoViewIfNeeded();
426
+ }
427
+ else {
428
+ nextHighlightedItem.scrollIntoView();
429
+ }
419
430
  }
420
431
  }
421
432
 
@@ -488,6 +499,11 @@ BMMenu.prototype = {
488
499
  menuNode.className = this._CSSClass ? `BMMenu ${this._CSSClass}` : 'BMMenu';
489
500
  this._node = menuNode;
490
501
 
502
+ const itemContainerNode = document.createElement('div');
503
+ itemContainerNode.className = 'BMMenuItemsContainer';
504
+ this._itemsContainerNode = itemContainerNode;
505
+ menuNode.appendChild(itemContainerNode);
506
+
491
507
  // When opening the menu, reset the highlight index
492
508
  this._highlightedIndex = -1;
493
509
 
@@ -608,7 +624,7 @@ BMMenu.prototype = {
608
624
  if (item.name.startsWith('---')) {
609
625
  const itemNode = document.createElement('div');
610
626
  itemNode.className = 'BMMenuDivider';
611
- this._node.appendChild(itemNode);
627
+ this._itemsContainerNode.appendChild(itemNode);
612
628
  return;
613
629
  }
614
630
 
@@ -727,7 +743,7 @@ BMMenu.prototype = {
727
743
  }
728
744
  });
729
745
 
730
- this._node.appendChild(itemNode);
746
+ this._itemsContainerNode.appendChild(itemNode);
731
747
  });
732
748
  },
733
749
 
@@ -772,6 +788,7 @@ BMMenu.prototype = {
772
788
  this._renderMenu();
773
789
 
774
790
  const menuNode = this._node;
791
+ const itemsContainerNode = this._itemsContainerNode;
775
792
  const menuContainer = this._containerNode;
776
793
  if (!this._supermenu) {
777
794
  document.body.appendChild(menuContainer);
@@ -860,6 +877,8 @@ BMMenu.prototype = {
860
877
  menuNode.style.transformOrigin = '50% 0%';
861
878
  }
862
879
 
880
+ this._kind = kind;
881
+
863
882
  const point = BMPointMake(Math.max(sourceRectScaled.origin.x + horizontalDisplacement, 8), sourceRectScaled.bottom + _BMMenuSpacingToNode + displacement);
864
883
  BMCopyProperties(menuNode.style, {left: point.x + 'px', top: point.y + 'px'});
865
884
 
@@ -889,7 +908,7 @@ BMMenu.prototype = {
889
908
  }
890
909
 
891
910
  // Make the menu expand
892
- menuNode.style.overflow = 'hidden';
911
+ itemsContainerNode.style.overflow = 'hidden';
893
912
 
894
913
  __BMVelocityAnimate(menuNode, {opacity: 1}, {
895
914
  duration,
@@ -907,17 +926,19 @@ BMMenu.prototype = {
907
926
 
908
927
  // Animate each child node in
909
928
  let delay = 50;
910
- let delayIncrement = Math.min(16, 150 / menuNode.childNodes.length);
929
+ let delayIncrement = Math.min(16, 150 / itemsContainerNode.childNodes.length);
911
930
 
912
- for (let i = 0; i < menuNode.childNodes.length; i++) {
913
- const child = menuNode.childNodes[i];
931
+ for (let i = 0; i < itemsContainerNode.childNodes.length; i++) {
932
+ const child = itemsContainerNode.childNodes[i];
914
933
  BMHook(child, {translateY: '16px', translateZ: 0, opacity: 0});
915
934
  __BMVelocityAnimate(child, {translateY: '0px', translateZ: 0, opacity: 1}, {
916
935
  duration: 100,
917
936
  easing: 'easeOutQuad',
918
937
  delay: delay,
919
- complete: i == menuNode.childNodes.length - 1 ? () => {
920
- menuNode.style.overflow = '';
938
+ complete: i == itemsContainerNode.childNodes.length - 1 ? () => {
939
+ if (this._node == menuNode) {
940
+ itemsContainerNode.style.overflow = '';
941
+ }
921
942
  } : undefined,
922
943
  }, BMMENU_USE_WEB_ANIMATIONS);
923
944
  delay += delayIncrement;
@@ -979,11 +1000,14 @@ BMMenu.prototype = {
979
1000
  this._renderMenu();
980
1001
 
981
1002
  const menuNode = this._node;
1003
+ const itemsContainerNode = this._itemsContainerNode;
982
1004
  const menuContainer = this._containerNode;
983
1005
 
1006
+ this._kind = kind;
1007
+
984
1008
  // Prepare the initial state of the animation
985
1009
  BMHook(menuNode, {
986
- scaleX: kind == BMMenuKind.PullDownMenu ? .5 : .75,
1010
+ scaleX: kind == BMMenuKind.PullDownMenu ? 1 : .75,
987
1011
  scaleY: kind == BMMenuKind.PullDownMenu ? .5 : .75,
988
1012
  opacity: 0
989
1013
  });
@@ -996,11 +1020,12 @@ BMMenu.prototype = {
996
1020
  const height = menuNode.offsetHeight;
997
1021
  const width = menuNode.offsetWidth;
998
1022
  const metrics = window.getComputedStyle(menuNode);
1023
+ const containerMetrics = window.getComputedStyle(itemsContainerNode);
999
1024
 
1000
1025
  // The paddings are used to adjust the menu's position so that the first
1001
1026
  // or last menu item appears directly on the pointer's position
1002
- const paddingTop = parseInt(metrics.paddingTop, 10) || 0;
1003
- const paddingBottom = parseInt(metrics.paddingBottom, 10) || 0;
1027
+ const paddingTop = (parseInt(metrics.paddingTop, 10) || 0) + (parseInt(containerMetrics.paddingTop, 10) || 0);
1028
+ const paddingBottom = (parseInt(metrics.paddingBottom, 10) || 0) + (parseInt(containerMetrics.paddingBottom, 10) || 0);
1004
1029
 
1005
1030
  // The transform origins will be adjusted based on which direction the menu opens in
1006
1031
  let transformOriginX = '0%';
@@ -1025,7 +1050,7 @@ BMMenu.prototype = {
1025
1050
  transformOriginX = '100%';
1026
1051
 
1027
1052
  menuNode.style.maxWidth = rect.origin.x + 'px';
1028
- point.x = rect.origin.x - width;
1053
+ point.x = Math.max(rect.origin.x - width, 0);
1029
1054
  }
1030
1055
  else {
1031
1056
  menuNode.style.maxWidth = (window.innerWidth - point.x) + 'px';
@@ -1051,7 +1076,7 @@ BMMenu.prototype = {
1051
1076
  menuNode.style.top = point.y + 'px';
1052
1077
 
1053
1078
  // Animate the menu in
1054
- menuNode.style.overflow = 'hidden';
1079
+ itemsContainerNode.style.overflow = 'hidden';
1055
1080
  (window.Velocity || $.Velocity).animate(menuNode, {scaleX: 1, scaleY: 1, opacity: 1, translateZ: 0}, {
1056
1081
  duration: 200,
1057
1082
  easing: 'easeOutQuad',
@@ -1063,17 +1088,19 @@ BMMenu.prototype = {
1063
1088
 
1064
1089
  // Animate each menu item in
1065
1090
  let delay = 0;
1066
- let delayIncrement = Math.min(16, 150 / menuNode.childNodes.length);
1091
+ let delayIncrement = Math.min(16, 150 / itemsContainerNode.childNodes.length);
1067
1092
 
1068
- for (let i = 0; i < menuNode.childNodes.length; i++) {
1069
- const child = menuNode.childNodes[i];
1093
+ for (let i = 0; i < itemsContainerNode.childNodes.length; i++) {
1094
+ const child = itemsContainerNode.childNodes[i];
1070
1095
  BMHook(child, {translateY: '16px', translateZ: 0, opacity: 0});
1071
1096
  (window.Velocity || $.Velocity).animate(child, {translateY: '0px', translateZ: 0, opacity: 1}, {
1072
1097
  duration: 100,
1073
1098
  easing: 'easeOutQuad',
1074
1099
  delay: delay,
1075
- complete: i == menuNode.childNodes.length - 1 ? () => {
1076
- menuNode.style.overflow = '';
1100
+ complete: i == itemsContainerNode.childNodes.length - 1 ? () => {
1101
+ if (this._node == menuNode) {
1102
+ itemsContainerNode.style.overflow = '';
1103
+ }
1077
1104
  } : undefined,
1078
1105
  });
1079
1106
  delay += delayIncrement;
@@ -1350,7 +1377,7 @@ BMMenu.prototype = {
1350
1377
 
1351
1378
  // Delay the actual menu node closing animation to allow time
1352
1379
  // for the menu items to run their animations
1353
- let delay = this._node.childNodes.length * 16;
1380
+ let delay = Math.min(this._itemsContainerNode.childNodes.length * 16, 150);
1354
1381
  delay = (delay < 0 ? 0 : delay);
1355
1382
 
1356
1383
  // If an item was clicked, further delay closing to allow the selection animation to play
@@ -1363,6 +1390,7 @@ BMMenu.prototype = {
1363
1390
  const sourceNodeShadow = this._sourceNodeShadow;
1364
1391
  const containerNode = this._containerNode;
1365
1392
  const node = this._node;
1393
+ const itemsContainerNode = this._itemsContainerNode;
1366
1394
  const sourceNode = this._sourceNode;
1367
1395
 
1368
1396
  this._sourceNode = undefined;
@@ -1393,7 +1421,7 @@ BMMenu.prototype = {
1393
1421
 
1394
1422
  this._isSupermenuClosing = NO;
1395
1423
 
1396
- this._node.style.overflow = 'hidden';
1424
+ itemsContainerNode.style.overflow = 'hidden';
1397
1425
 
1398
1426
  __BMVelocityAnimate(this._node, {
1399
1427
  scaleX: this._kind == BMMenuKind.PullDownMenu ? 1 : scale,
@@ -1431,18 +1459,20 @@ BMMenu.prototype = {
1431
1459
  // if an item was selected
1432
1460
  delay = 200;
1433
1461
  }
1434
- for (let i = this._node.childNodes.length - 1; i >= 0; i--) {
1435
- let child = this._node.childNodes[i];
1462
+ const delayIncrement = Math.min(16, 150 / itemsContainerNode.childNodes.length);
1463
+ for (let i = itemsContainerNode.childNodes.length - 1; i >= 0; i--) {
1464
+ let child = itemsContainerNode.childNodes[i];
1436
1465
  __BMVelocityAnimate(child, {translateY: '16px', translateZ: 0, opacity: 0}, {
1437
1466
  duration: 100,
1438
1467
  easing: 'easeInQuad',
1439
1468
  delay: delay
1440
1469
  }, BMMENU_USE_WEB_ANIMATIONS);
1441
- delay += 16;
1470
+ delay += delayIncrement;
1442
1471
  }
1443
1472
 
1444
1473
  this._node = undefined;
1445
1474
  this._containerNode = undefined;
1475
+ this._itemsContainerNode = undefined;
1446
1476
  },
1447
1477
 
1448
1478
  /**