bm-core-ui 2.12.0-beta.13 → 2.12.0-beta.14
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/build/@types/index.d.ts +118 -0
- package/build/BMCollectionView/BMCollectionView.js +15 -15
- package/build/BMCoreUI.css +66 -72
- package/build/BMView/BMMenu.js +336 -71
- package/lib/@types/BMCoreUI.min.d.ts +118 -0
- package/lib/BMCoreUI.min.js +1 -1
- package/package.json +1 -1
package/build/BMView/BMMenu.js
CHANGED
|
@@ -6,7 +6,7 @@ import {BMHook, __BMVelocityAnimate} from '../Core/BMAnimationContext'
|
|
|
6
6
|
import 'velocity-animate'
|
|
7
7
|
import { BMRect, BMRectMake, BMRectMakeWithNodeFrame, BMRectMakeWithOrigin } from '../Core/BMRect';
|
|
8
8
|
import { BMView } from './BMView_v2.5';
|
|
9
|
-
import { BMKeyboardShortcut } from '../BMWindow/BMKeyboardShortcut';
|
|
9
|
+
import { BMKeyboardShortcut, BMKeyboardShortcutModifier } from '../BMWindow/BMKeyboardShortcut';
|
|
10
10
|
import { BMSizeMake } from '../Core/BMSize';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -111,21 +111,33 @@ BMMenuItem.prototype = {
|
|
|
111
111
|
return this._CSSClass;
|
|
112
112
|
},
|
|
113
113
|
set CSSClass(cls) {
|
|
114
|
+
if (!this._replacementItem) {
|
|
115
|
+
this._updateCSSClass(this._CSSClass, {toClass: cls});
|
|
116
|
+
}
|
|
117
|
+
this._CSSClass = cls || '';
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Updates the CSS class on this item's node.
|
|
122
|
+
* @param fromClass <String> The currently applied CSS classes.
|
|
123
|
+
* {
|
|
124
|
+
* @param toClass <String> The new classes to apply.
|
|
125
|
+
* }
|
|
126
|
+
*/
|
|
127
|
+
_updateCSSClass(fromClass, {toClass}) {
|
|
114
128
|
// If this menu item is rendered, apply the class to its node
|
|
115
129
|
let node = this._node;
|
|
116
130
|
if (node) {
|
|
117
131
|
// First remove the previous classes, if they exist
|
|
118
|
-
if (
|
|
119
|
-
|
|
132
|
+
if (fromClass) {
|
|
133
|
+
fromClass.split(' ').forEach(cls => node.classList.remove(cls));
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
// Then add the new classes, if they exist
|
|
123
|
-
if (
|
|
124
|
-
|
|
137
|
+
if (toClass) {
|
|
138
|
+
toClass.split(' ').forEach(cls => node.classList.add(cls));
|
|
125
139
|
}
|
|
126
140
|
}
|
|
127
|
-
|
|
128
|
-
this._CSSClass = cls || '';
|
|
129
141
|
},
|
|
130
142
|
|
|
131
143
|
/**
|
|
@@ -149,6 +161,16 @@ BMMenuItem.prototype = {
|
|
|
149
161
|
},
|
|
150
162
|
set hintHTML(hint) {
|
|
151
163
|
this._hintHTML = hint;
|
|
164
|
+
if (!this._replacementItem) {
|
|
165
|
+
this._setHintHTML(hint);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Updates this item's HTML hint node with the specified hint.
|
|
171
|
+
* @param hint <String> The hint to use.
|
|
172
|
+
*/
|
|
173
|
+
_setHintHTML(hint) {
|
|
152
174
|
if (this._node) {
|
|
153
175
|
const hintContainer = this._node.querySelector('.BMMenuItemHint');
|
|
154
176
|
if (hintContainer) {
|
|
@@ -163,6 +185,18 @@ BMMenuItem.prototype = {
|
|
|
163
185
|
*/
|
|
164
186
|
_node: undefined, // <DOMNode, nullable>
|
|
165
187
|
|
|
188
|
+
/**
|
|
189
|
+
* The node that represents this menu item's icon, available while its menu
|
|
190
|
+
* is open.
|
|
191
|
+
*/
|
|
192
|
+
_iconNode: undefined, // <DOMNode, nullable>
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The node that represents this menu item's title, available while its menu
|
|
196
|
+
* is open.
|
|
197
|
+
*/
|
|
198
|
+
_nameNode: undefined, // <DOMNode, nullable>
|
|
199
|
+
|
|
166
200
|
/**
|
|
167
201
|
* Defaults to `YES`. Controls whether this menu item is enabled. If this menu
|
|
168
202
|
* item is not enabled, it can not be highlighted or selected.
|
|
@@ -183,6 +217,16 @@ BMMenuItem.prototype = {
|
|
|
183
217
|
|
|
184
218
|
this._enabled = enabled;
|
|
185
219
|
|
|
220
|
+
if (!this._replacementItem) {
|
|
221
|
+
this._setEnabled(enabled);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Updates the enabled state of this menu item's node.
|
|
227
|
+
* @param enabled <Boolean> Whether the item is enabled.
|
|
228
|
+
*/
|
|
229
|
+
_setEnabled(enabled) {
|
|
186
230
|
if (this._node) {
|
|
187
231
|
/** @type {HTMLElement} */ const node = this._node;
|
|
188
232
|
if (enabled) {
|
|
@@ -195,6 +239,89 @@ BMMenuItem.prototype = {
|
|
|
195
239
|
},
|
|
196
240
|
|
|
197
241
|
/**
|
|
242
|
+
* The currently active replacement menu item, if any.
|
|
243
|
+
*/
|
|
244
|
+
_replacementItem: undefined, // <BMMenuItem, nullable>
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Causes this menu item to instead display the specified menu item instead.
|
|
248
|
+
* @param item <BMMenuItem, nullable> The replacement menu item, or `undefined` to restore
|
|
249
|
+
* this menu item.
|
|
250
|
+
* @returns <Boolean> `YES` if a replacement occurred, `NO` otherwise.
|
|
251
|
+
*/
|
|
252
|
+
_setReplacementItem(item) {
|
|
253
|
+
if (this._replacementItem == item) {
|
|
254
|
+
return NO;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const previousReplacementItem = this._replacementItem || this;
|
|
258
|
+
this._replacementItem = item;
|
|
259
|
+
if (!this._node) {
|
|
260
|
+
return YES;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!item) {
|
|
264
|
+
item = this;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Update the display of this menu item to the specified replacement item
|
|
268
|
+
if (this._iconNode) {
|
|
269
|
+
this._iconNode.src = item.icon;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
this._nameNode.innerText = item.name;
|
|
273
|
+
this._setEnabled(item.enabled);
|
|
274
|
+
this._setHintHTML(item.hintHTML);
|
|
275
|
+
this._updateCSSClass(previousReplacementItem.CSSClass, item.CSSClass);
|
|
276
|
+
|
|
277
|
+
return YES;
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Contains the replacement menu items and their modifiers.
|
|
282
|
+
*/
|
|
283
|
+
_overrideMenuItems: undefined, // <Object<string, {item: BMMenuItem, priority: number}>>
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Registers a menu item that will replace this menu item in an open menu when the
|
|
287
|
+
* specified keyboard modifiers are held.
|
|
288
|
+
* @param item <BMMenuItem> The replacement menu item to add.
|
|
289
|
+
* {
|
|
290
|
+
* @param forModifiers <[BMKeyboardShortcutModifier]> An array of modifiers that must be held for the
|
|
291
|
+
* replacement item to be selected.
|
|
292
|
+
* }
|
|
293
|
+
*/
|
|
294
|
+
addOverrideMenuItem(item, {forModifiers: modifiers}) {
|
|
295
|
+
if (!modifiers.length) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let bitmap = 0;
|
|
300
|
+
for (const modifier of modifiers) {
|
|
301
|
+
bitmap = bitmap | modifier.value;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this._overrideMenuItems[bitmap] = {item, priority: modifiers.length};
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Unregisters the replacement menu item when the specified keyboard modifiers are held.
|
|
309
|
+
* Has no effect if a replacement menu item was not set for the specified modifiers.
|
|
310
|
+
* @param modifiers <[BMKeyboardShortcutModifier]> The keyboard modifiers.
|
|
311
|
+
*/
|
|
312
|
+
removeOverrideMenuItemForModifiers(modifiers) {
|
|
313
|
+
let bitmap = 0;
|
|
314
|
+
for (const modifier of modifiers) {
|
|
315
|
+
bitmap = bitmap | modifier.value;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (this._overrideMenuItems[bitmap]) {
|
|
319
|
+
delete this._overrideMenuItems[bitmap];
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Designated initializer.
|
|
198
325
|
* Constructs and returns a menu item with the given name.
|
|
199
326
|
* Optionally, the menu item may have an icon associated with it as well as an
|
|
200
327
|
* action that is invoked when the item is selected.
|
|
@@ -202,7 +329,7 @@ BMMenuItem.prototype = {
|
|
|
202
329
|
* {
|
|
203
330
|
* @param icon <String, nullable> If specified, this represents the URL to an icon associated with this menu item.
|
|
204
331
|
* For most menus, icons will only be displayed if all of its menu items have icons defined.
|
|
205
|
-
*
|
|
332
|
+
*
|
|
206
333
|
* @param action <void ^ (BMMenuItem), nullable> If specified, this is a function that will be invoked when this menu item is selected.
|
|
207
334
|
* This function will receive the selected menu item as a parameter.
|
|
208
335
|
* @param enabled <Boolean, nullable> Defaults to `YES`. Controls whether this item is enabled and can be selected.
|
|
@@ -212,6 +339,8 @@ BMMenuItem.prototype = {
|
|
|
212
339
|
* @return <BMMenuItem> This menu item.
|
|
213
340
|
*/
|
|
214
341
|
initWithName(name, {icon, action, submenu, enabled = true, userInfo} = {}) {
|
|
342
|
+
this._overrideMenuItems = {};
|
|
343
|
+
|
|
215
344
|
this._name = name;
|
|
216
345
|
this._icon = icon;
|
|
217
346
|
this._action = action;
|
|
@@ -238,7 +367,7 @@ BMMenuItem.prototype = {
|
|
|
238
367
|
* {
|
|
239
368
|
* @param icon <String, nullable> If specified, this represents the URL to an icon associated with this menu item.
|
|
240
369
|
* For most menus, icons will only be displayed if all of its menu items have icons defined.
|
|
241
|
-
*
|
|
370
|
+
*
|
|
242
371
|
* @param action <void ^ (BMMenuItem), nullable> If specified, this is a function that will be invoked when this menu item is selected.
|
|
243
372
|
* This function will receive the selected menu item as a parameter.
|
|
244
373
|
* @param enabled <Boolean, nullable> Defaults to `YES`. Controls whether this item is enabled and can be selected.
|
|
@@ -469,9 +598,9 @@ BMMenu.prototype = {
|
|
|
469
598
|
/**
|
|
470
599
|
* Initializes this menu with the specified items.
|
|
471
600
|
* The menu will be hidden by default; the method showAtPoint(_) should be used to make the menu visible.
|
|
472
|
-
*
|
|
601
|
+
*
|
|
473
602
|
* @param items <[BMMenuItem]> An array of menu items that will be displayed by this menu.
|
|
474
|
-
*
|
|
603
|
+
*
|
|
475
604
|
* @return <BMMenu> A menu.
|
|
476
605
|
*/
|
|
477
606
|
initWithItems(items) {
|
|
@@ -480,6 +609,76 @@ BMMenu.prototype = {
|
|
|
480
609
|
return this;
|
|
481
610
|
},
|
|
482
611
|
|
|
612
|
+
/**
|
|
613
|
+
* The current modifier bitmap.
|
|
614
|
+
*/
|
|
615
|
+
__modifierBitmap: 0, // <Number>
|
|
616
|
+
|
|
617
|
+
get _modifierBitmap() {
|
|
618
|
+
return this._modifierBitmap;
|
|
619
|
+
},
|
|
620
|
+
|
|
621
|
+
set _modifierBitmap(bitmap) {
|
|
622
|
+
this.__modifierBitmap = bitmap;
|
|
623
|
+
|
|
624
|
+
let changeOccurred = NO;
|
|
625
|
+
|
|
626
|
+
for (const item of this._items) {
|
|
627
|
+
if (!item._node) {
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (!bitmap) {
|
|
632
|
+
changeOccurred = item._setReplacementItem(undefined) || changeOccurred;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
let replacementPriority = 0;
|
|
636
|
+
let replacementItem;
|
|
637
|
+
for (const replacementBitmap in item._overrideMenuItems) {
|
|
638
|
+
const modifierBitmap = parseInt(replacementBitmap);
|
|
639
|
+
if ((modifierBitmap & bitmap) != modifierBitmap) {
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (item._overrideMenuItems[replacementBitmap].priority > replacementPriority) {
|
|
644
|
+
replacementPriority = item._overrideMenuItems[replacementBitmap].priority;
|
|
645
|
+
replacementItem = item._overrideMenuItems[replacementBitmap].item;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
changeOccurred = item._setReplacementItem(replacementItem) || changeOccurred;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (changeOccurred && this._node && this._rect) {
|
|
653
|
+
this._setAnchorRect(this._rect);
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Invoked to handle the specified keyboard event.
|
|
661
|
+
* @param event <KeyboardEvent> The event that triggered this action.
|
|
662
|
+
*/
|
|
663
|
+
handleEvent(event) {
|
|
664
|
+
this._updateModifierBitmapWithEvent(event);
|
|
665
|
+
},
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Updates the modifier bitmap based on the specified event.
|
|
669
|
+
* @param event <Event> The event describing the currently pressed modifier keys.
|
|
670
|
+
*/
|
|
671
|
+
_updateModifierBitmapWithEvent(event) {
|
|
672
|
+
let bitmap = 0;
|
|
673
|
+
for (const modifier in BMKeyboardShortcutModifier) {
|
|
674
|
+
if (event[BMKeyboardShortcutModifier[modifier].key]) {
|
|
675
|
+
bitmap = bitmap | BMKeyboardShortcutModifier[modifier].value;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
this._modifierBitmap = bitmap;
|
|
680
|
+
},
|
|
681
|
+
|
|
483
682
|
/**
|
|
484
683
|
* Invoked by CoreUI to build the menu node.
|
|
485
684
|
*/
|
|
@@ -555,6 +754,15 @@ BMMenu.prototype = {
|
|
|
555
754
|
shortcut._menuItem = item;
|
|
556
755
|
BMView.registerKeyboardShortcut(shortcut, {forNode: menuNode});
|
|
557
756
|
}
|
|
757
|
+
|
|
758
|
+
for (const key in item._overrideMenuItems) {
|
|
759
|
+
const replacementItem = item._overrideMenuItems[key].item;
|
|
760
|
+
if (replacementItem.keySequence) {
|
|
761
|
+
const shortcut = BMKeyboardShortcut.keyboardShortcutWithKeySequence(replacementItem.keySequence, {target: this, action: '_selectMenuItemWithEvent'});
|
|
762
|
+
shortcut._menuItem = item;
|
|
763
|
+
BMView.registerKeyboardShortcut(shortcut, {forNode: menuNode});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
558
766
|
}
|
|
559
767
|
|
|
560
768
|
// The overlay which intercepts clicks outside of the menu
|
|
@@ -563,6 +771,10 @@ BMMenu.prototype = {
|
|
|
563
771
|
menuContainer.className = 'BMMenuContainer';
|
|
564
772
|
}
|
|
565
773
|
|
|
774
|
+
// Register the modifier listener that sets replacement items on each item
|
|
775
|
+
menuContainer.addEventListener('keydown', this, {passive: true});
|
|
776
|
+
menuContainer.addEventListener('keyup', this, {passive: true});
|
|
777
|
+
|
|
566
778
|
if (!('backdropFilter' in document.body.style) && !('webkitBackdropFilter' in document.body.style)) {
|
|
567
779
|
menuNode.style.backgroundColor = 'white';
|
|
568
780
|
}
|
|
@@ -634,6 +846,9 @@ BMMenu.prototype = {
|
|
|
634
846
|
return;
|
|
635
847
|
}
|
|
636
848
|
|
|
849
|
+
// Clear out any previously set replacement item
|
|
850
|
+
item._setReplacementItem(undefined);
|
|
851
|
+
|
|
637
852
|
// Create the item node
|
|
638
853
|
let itemNode = document.createElement('div');
|
|
639
854
|
itemNode.className = 'BMMenuItem';
|
|
@@ -649,12 +864,18 @@ BMMenu.prototype = {
|
|
|
649
864
|
height: iconSize + 'px'
|
|
650
865
|
});
|
|
651
866
|
itemNode.appendChild(icon);
|
|
867
|
+
|
|
868
|
+
item._iconNode = icon;
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
item._iconNode = undefined;
|
|
652
872
|
}
|
|
653
873
|
|
|
654
874
|
// Create the label
|
|
655
875
|
const itemText = document.createElement('span');
|
|
656
876
|
itemText.innerText = item.name;
|
|
657
877
|
itemNode.appendChild(itemText);
|
|
878
|
+
item._nameNode = itemText;
|
|
658
879
|
|
|
659
880
|
// Create the hint container
|
|
660
881
|
const hint = document.createElement('div');
|
|
@@ -674,7 +895,7 @@ BMMenu.prototype = {
|
|
|
674
895
|
if (!item._enabled) {
|
|
675
896
|
itemNode.classList.add('BMMenuItemDisabled');
|
|
676
897
|
}
|
|
677
|
-
|
|
898
|
+
|
|
678
899
|
// TWhen clicking an item, trigger its action, if it has one
|
|
679
900
|
itemNode.addEventListener('click', event => {
|
|
680
901
|
// Disabled items can't be clicked
|
|
@@ -694,7 +915,7 @@ BMMenu.prototype = {
|
|
|
694
915
|
// Ask the delegate if this menu item should be selected
|
|
695
916
|
let shouldSelect = YES;
|
|
696
917
|
if (this.delegate && this.delegate.menuShouldSelectItem) {
|
|
697
|
-
shouldSelect = this.delegate.menuShouldSelectItem(this, item);
|
|
918
|
+
shouldSelect = this.delegate.menuShouldSelectItem(this, item._replacementItem ?? item);
|
|
698
919
|
}
|
|
699
920
|
|
|
700
921
|
if (!shouldSelect) {
|
|
@@ -703,6 +924,9 @@ BMMenu.prototype = {
|
|
|
703
924
|
return;
|
|
704
925
|
}
|
|
705
926
|
|
|
927
|
+
// Allow selection to go through when events are suppressed
|
|
928
|
+
this._delaysEvents = NO;
|
|
929
|
+
|
|
706
930
|
// For desktop menus, delay closing slightly to allow the selection
|
|
707
931
|
// animation to play out
|
|
708
932
|
this._delaysClosing = YES;
|
|
@@ -756,9 +980,14 @@ BMMenu.prototype = {
|
|
|
756
980
|
* @param item <BMMenuItem> The menu item whose action should be performed.
|
|
757
981
|
*/
|
|
758
982
|
_performActionForMenuItem(item) {
|
|
759
|
-
item.action
|
|
983
|
+
if (item._replacementItem?.action) {
|
|
984
|
+
item._replacementItem.action(item._replacementItem);
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
item.action?.(item._replacementItem ?? item);
|
|
988
|
+
}
|
|
760
989
|
|
|
761
|
-
this.delegate?.menuDidSelectItem?.(this, item);
|
|
990
|
+
this.delegate?.menuDidSelectItem?.(this, item._replacementItem ?? item);
|
|
762
991
|
},
|
|
763
992
|
|
|
764
993
|
/**
|
|
@@ -774,12 +1003,14 @@ BMMenu.prototype = {
|
|
|
774
1003
|
* @param animated <Boolean, nullable> Defaults to `NO`. If set to `YES`, this change will be animated.
|
|
775
1004
|
* If this method is invoked from within an animation context, the value of this parameter is ignored
|
|
776
1005
|
* and the values of the current animation context are used.
|
|
777
|
-
*
|
|
1006
|
+
*
|
|
778
1007
|
* @param kind <BMMenuKind, nullable> Defaults to `Menu`. The kind of menu.
|
|
779
1008
|
* }
|
|
780
1009
|
* @return <Promise<void>> A promise that resolves when the operation completes.
|
|
781
1010
|
*/
|
|
782
1011
|
async openFromNode(node, {animated = NO, kind = BMMenuKind.Menu} = {}) {
|
|
1012
|
+
this._rect = undefined;
|
|
1013
|
+
|
|
783
1014
|
// Signal the delegate that this menu is about to open
|
|
784
1015
|
if (this.delegate && this.delegate.menuWillOpen) {
|
|
785
1016
|
this.delegate.menuWillOpen(this);
|
|
@@ -897,7 +1128,7 @@ BMMenu.prototype = {
|
|
|
897
1128
|
BMCopyProperties(menuNode.style, {left: point.x + 'px', top: point.y + 'px'});
|
|
898
1129
|
|
|
899
1130
|
this._frame = BMRectMakeWithOrigin(point, {size: BMSizeMake(menuWidth, menuHeight)});
|
|
900
|
-
|
|
1131
|
+
|
|
901
1132
|
// Prepare the initial state of the animation
|
|
902
1133
|
if (!this._supermenu) {
|
|
903
1134
|
BMHook(menuContainer, {opacity: 0});
|
|
@@ -906,8 +1137,8 @@ BMMenu.prototype = {
|
|
|
906
1137
|
BMHook(menuNode, {opacity: 0});
|
|
907
1138
|
}
|
|
908
1139
|
BMHook(menuNode, {
|
|
909
|
-
scaleX: kind == BMMenuKind.PullDownMenu ? pullDownScale : scale,
|
|
910
|
-
scaleY: kind == BMMenuKind.PullDownMenu ? pullDownScale : scale,
|
|
1140
|
+
scaleX: kind == BMMenuKind.PullDownMenu ? pullDownScale : scale,
|
|
1141
|
+
scaleY: kind == BMMenuKind.PullDownMenu ? pullDownScale : scale,
|
|
911
1142
|
translateY: -displacement + 'px',
|
|
912
1143
|
translateX: -horizontalDisplacement + 'px',
|
|
913
1144
|
});
|
|
@@ -923,7 +1154,7 @@ BMMenu.prototype = {
|
|
|
923
1154
|
|
|
924
1155
|
// Make the menu expand
|
|
925
1156
|
itemsContainerNode.style.overflow = 'hidden';
|
|
926
|
-
|
|
1157
|
+
|
|
927
1158
|
__BMVelocityAnimate(menuNode, {opacity: 1}, {
|
|
928
1159
|
duration: duration / 2,
|
|
929
1160
|
// Use a different easing for the opacity so that it doesn't overshoot the 1 limit
|
|
@@ -942,7 +1173,7 @@ BMMenu.prototype = {
|
|
|
942
1173
|
// Animate each child node in
|
|
943
1174
|
let delay = 50;
|
|
944
1175
|
let delayIncrement = Math.min(16, 150 / itemsContainerNode.childNodes.length);
|
|
945
|
-
|
|
1176
|
+
|
|
946
1177
|
for (let i = 0; i < itemsContainerNode.childNodes.length; i++) {
|
|
947
1178
|
const child = itemsContainerNode.childNodes[i];
|
|
948
1179
|
BMHook(child, {translateY: '16px', translateZ: 0, opacity: 0});
|
|
@@ -965,14 +1196,19 @@ BMMenu.prototype = {
|
|
|
965
1196
|
|
|
966
1197
|
// Animate the source node shadow
|
|
967
1198
|
await __BMVelocityAnimate(
|
|
968
|
-
this._sourceNodeShadow,
|
|
969
|
-
{translateY: displacement + 'px', translateX: horizontalDisplacement + 'px', scaleX: sourceNodeScale, scaleY: sourceNodeScale},
|
|
1199
|
+
this._sourceNodeShadow,
|
|
1200
|
+
{translateY: displacement + 'px', translateX: horizontalDisplacement + 'px', scaleX: sourceNodeScale, scaleY: sourceNodeScale},
|
|
970
1201
|
{duration, easing},
|
|
971
1202
|
BMMENU_USE_WEB_ANIMATIONS
|
|
972
1203
|
);
|
|
973
1204
|
|
|
974
1205
|
},
|
|
975
1206
|
|
|
1207
|
+
/**
|
|
1208
|
+
* The rect around which this menu is positioned.
|
|
1209
|
+
*/
|
|
1210
|
+
_rect: undefined, // <BMRect, nullable>
|
|
1211
|
+
|
|
976
1212
|
/**
|
|
977
1213
|
* Animatable. Shows this menu at the specified point. The coordinates of this point are relative to the viewport.
|
|
978
1214
|
* @param point <BMPoint> The point at which to show this menu.
|
|
@@ -980,7 +1216,7 @@ BMMenu.prototype = {
|
|
|
980
1216
|
* @param animated <Boolean, nullable> Defaults to `NO`. If set to `YES`, this change will be animated.
|
|
981
1217
|
* If this method is invoked from within an animation context, the value of this parameter is ignored
|
|
982
1218
|
* and the values of the current animation context are used.
|
|
983
|
-
*
|
|
1219
|
+
*
|
|
984
1220
|
* @param kind <BMMenuKind, nullable> Defaults to `Menu`. The kind of menu.
|
|
985
1221
|
* }
|
|
986
1222
|
*/
|
|
@@ -988,48 +1224,21 @@ BMMenu.prototype = {
|
|
|
988
1224
|
// Create a 0-by-0 rect at the given point and open the menu around it
|
|
989
1225
|
const rect = BMRectMakeWithOrigin(point, {size: BMSizeMake()});
|
|
990
1226
|
|
|
1227
|
+
this._rect = rect;
|
|
991
1228
|
return this._openAroundRect(rect, arguments[1]);
|
|
992
1229
|
},
|
|
993
1230
|
|
|
994
1231
|
/**
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
* @param rect <BMRect> The rect around which to show this menu.
|
|
998
|
-
* {
|
|
999
|
-
* @param animated <Boolean, nullable> Defaults to `NO`. If set to `YES`, this change will be animated.
|
|
1000
|
-
* If this method is invoked from within an animation context, the value of this parameter is ignored
|
|
1001
|
-
* and the values of the current animation context are used.
|
|
1002
|
-
*
|
|
1003
|
-
* @param kind <BMMenuKind, nullable> Defaults to `Menu`. The kind of menu.
|
|
1232
|
+
* Updates this menu's position around the specified rect.
|
|
1233
|
+
* @param rect <BMRect> The rect around which to position this menu.
|
|
1004
1234
|
*/
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
if (this._node) return;
|
|
1008
|
-
|
|
1009
|
-
// Signal the delegate that this menu is about to open
|
|
1010
|
-
if (this.delegate && this.delegate.menuWillOpen) {
|
|
1011
|
-
this.delegate.menuWillOpen(this);
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
// Build the DOM structure
|
|
1015
|
-
this._renderMenu();
|
|
1235
|
+
_setAnchorRect(rect) {
|
|
1236
|
+
this._rect = rect;
|
|
1016
1237
|
|
|
1017
1238
|
const menuNode = this._node;
|
|
1018
1239
|
const itemsContainerNode = this._itemsContainerNode;
|
|
1019
|
-
const menuContainer = this._containerNode;
|
|
1020
|
-
|
|
1021
|
-
this._kind = kind;
|
|
1022
|
-
|
|
1023
|
-
// Prepare the initial state of the animation
|
|
1024
|
-
BMHook(menuNode, {
|
|
1025
|
-
scaleX: kind == BMMenuKind.PullDownMenu ? 1 : .75,
|
|
1026
|
-
scaleY: kind == BMMenuKind.PullDownMenu ? .5 : .75,
|
|
1027
|
-
opacity: 0
|
|
1028
|
-
});
|
|
1029
1240
|
|
|
1030
|
-
|
|
1031
|
-
document.body.appendChild(menuContainer);
|
|
1032
|
-
}
|
|
1241
|
+
const kind = this._kind;
|
|
1033
1242
|
|
|
1034
1243
|
// Get the menu's metrics to determine where it fits best
|
|
1035
1244
|
const height = menuNode.offsetHeight;
|
|
@@ -1090,6 +1299,58 @@ BMMenu.prototype = {
|
|
|
1090
1299
|
menuNode.style.left = point.x + 'px';
|
|
1091
1300
|
menuNode.style.top = point.y + 'px';
|
|
1092
1301
|
|
|
1302
|
+
// If any submenus are open, update their positions as well
|
|
1303
|
+
if (this._submenu) {
|
|
1304
|
+
const itemNode = this._submenu._sourceItem._node;
|
|
1305
|
+
if (itemNode) {
|
|
1306
|
+
const frame = BMRectMakeWithNodeFrame(itemNode);
|
|
1307
|
+
this._submenu._setAnchorRect(frame);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
|
|
1312
|
+
/**
|
|
1313
|
+
* Shows this menu at an appropriate position around the given rect. The coordinates of this rect
|
|
1314
|
+
* are relative to the viewport.
|
|
1315
|
+
* @param rect <BMRect> The rect around which to show this menu.
|
|
1316
|
+
* {
|
|
1317
|
+
* @param animated <Boolean, nullable> Defaults to `NO`. If set to `YES`, this change will be animated.
|
|
1318
|
+
* If this method is invoked from within an animation context, the value of this parameter is ignored
|
|
1319
|
+
* and the values of the current animation context are used.
|
|
1320
|
+
*
|
|
1321
|
+
* @param kind <BMMenuKind, nullable> Defaults to `Menu`. The kind of menu.
|
|
1322
|
+
*/
|
|
1323
|
+
_openAroundRect(rect, {animated = NO, kind = BMMenuKind.Menu} = {}) {
|
|
1324
|
+
// If the menu is already open, do nothing
|
|
1325
|
+
if (this._node) return;
|
|
1326
|
+
|
|
1327
|
+
// Signal the delegate that this menu is about to open
|
|
1328
|
+
if (this.delegate && this.delegate.menuWillOpen) {
|
|
1329
|
+
this.delegate.menuWillOpen(this);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// Build the DOM structure
|
|
1333
|
+
this._renderMenu();
|
|
1334
|
+
|
|
1335
|
+
const menuNode = this._node;
|
|
1336
|
+
const itemsContainerNode = this._itemsContainerNode;
|
|
1337
|
+
const menuContainer = this._containerNode;
|
|
1338
|
+
|
|
1339
|
+
this._kind = kind;
|
|
1340
|
+
|
|
1341
|
+
// Prepare the initial state of the animation
|
|
1342
|
+
BMHook(menuNode, {
|
|
1343
|
+
scaleX: kind == BMMenuKind.PullDownMenu ? 1 : .75,
|
|
1344
|
+
scaleY: kind == BMMenuKind.PullDownMenu ? .5 : .75,
|
|
1345
|
+
opacity: 0
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
if (!this._supermenu) {
|
|
1349
|
+
document.body.appendChild(menuContainer);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
this._setAnchorRect(rect);
|
|
1353
|
+
|
|
1093
1354
|
// Animate the menu in
|
|
1094
1355
|
itemsContainerNode.style.overflow = 'hidden';
|
|
1095
1356
|
(window.Velocity || $.Velocity).animate(menuNode, {scaleX: 1, scaleY: 1, opacity: 1, translateZ: 0}, {
|
|
@@ -1157,6 +1418,7 @@ BMMenu.prototype = {
|
|
|
1157
1418
|
|
|
1158
1419
|
// Render the submenu and set its delegate to this menu's delegate
|
|
1159
1420
|
submenu._supermenu = this;
|
|
1421
|
+
submenu._sourceItem = item;
|
|
1160
1422
|
this._submenu = submenu;
|
|
1161
1423
|
|
|
1162
1424
|
// Open an appropriate menu kind based on this menu's kind
|
|
@@ -1242,11 +1504,11 @@ BMMenu.prototype = {
|
|
|
1242
1504
|
// On the first movement initialize the previous position
|
|
1243
1505
|
// and top and bottom points relative to the pointer's position
|
|
1244
1506
|
lastPosition = currentPosition;
|
|
1245
|
-
topPoint = submenuFrame.origin.x > lastPosition.x ?
|
|
1246
|
-
submenuFrame.origin :
|
|
1507
|
+
topPoint = submenuFrame.origin.x > lastPosition.x ?
|
|
1508
|
+
submenuFrame.origin :
|
|
1247
1509
|
BMPointMake(submenuFrame.right, submenuFrame.origin.y);
|
|
1248
|
-
bottomPoint = submenuFrame.origin.x > lastPosition.x ?
|
|
1249
|
-
BMPointMake(submenuFrame.origin.x, submenuFrame.bottom) :
|
|
1510
|
+
bottomPoint = submenuFrame.origin.x > lastPosition.x ?
|
|
1511
|
+
BMPointMake(submenuFrame.origin.x, submenuFrame.bottom) :
|
|
1250
1512
|
BMPointMake(submenuFrame.right, submenuFrame.bottom);
|
|
1251
1513
|
|
|
1252
1514
|
if (submenuFrame.origin.x < lastPosition.x) {
|
|
@@ -1315,7 +1577,7 @@ BMMenu.prototype = {
|
|
|
1315
1577
|
let isOutsideSlopeRange = flipSlopes ?
|
|
1316
1578
|
currentSlope > minSlope && currentSlope < maxSlope :
|
|
1317
1579
|
currentSlope < minSlope || currentSlope > maxSlope;
|
|
1318
|
-
|
|
1580
|
+
|
|
1319
1581
|
if (isOutsideSlopeRange) {
|
|
1320
1582
|
resumeEvents();
|
|
1321
1583
|
return;
|
|
@@ -1342,6 +1604,9 @@ BMMenu.prototype = {
|
|
|
1342
1604
|
// If the menu isn't visible this action has no effect.
|
|
1343
1605
|
if (!this._node) return;
|
|
1344
1606
|
|
|
1607
|
+
this._containerNode.removeEventListener('keydown', this, {passive: true});
|
|
1608
|
+
this._containerNode.removeEventListener('keyup', this, {passive: true});
|
|
1609
|
+
|
|
1345
1610
|
// If any submenu is displayed, dismiss it as well
|
|
1346
1611
|
if (this._submenu) {
|
|
1347
1612
|
this._submenu._isSupermenuClosing = YES;
|
|
@@ -1382,7 +1647,7 @@ BMMenu.prototype = {
|
|
|
1382
1647
|
}
|
|
1383
1648
|
|
|
1384
1649
|
// Prevent interaction with the menu while its close animation is running
|
|
1385
|
-
this._node.style.pointerEvents = 'none';
|
|
1650
|
+
this._node.style.pointerEvents = 'none';
|
|
1386
1651
|
// this._node.inert = true;
|
|
1387
1652
|
|
|
1388
1653
|
if (!supermenu) {
|
|
@@ -1410,7 +1675,7 @@ BMMenu.prototype = {
|
|
|
1410
1675
|
|
|
1411
1676
|
const selectedMenuItem = this._selectedMenuItem;
|
|
1412
1677
|
this._selectedMenuItem = undefined;
|
|
1413
|
-
|
|
1678
|
+
|
|
1414
1679
|
if (selectedMenuItem) {
|
|
1415
1680
|
if (this.delaysActions && selectedItemCallbackDelay) {
|
|
1416
1681
|
setTimeout(() => this._performActionForMenuItem(selectedMenuItem), selectedItemCallbackDelay);
|
|
@@ -1457,9 +1722,9 @@ BMMenu.prototype = {
|
|
|
1457
1722
|
itemsContainerNode.style.overflow = 'hidden';
|
|
1458
1723
|
|
|
1459
1724
|
__BMVelocityAnimate(this._node, {
|
|
1460
|
-
scaleX: this._kind == BMMenuKind.PullDownMenu ? 1 : scale,
|
|
1461
|
-
scaleY: scale,
|
|
1462
|
-
opacity: (sourceNodeShadow && !supermenu) ? 1 : 0,
|
|
1725
|
+
scaleX: this._kind == BMMenuKind.PullDownMenu ? 1 : scale,
|
|
1726
|
+
scaleY: scale,
|
|
1727
|
+
opacity: (sourceNodeShadow && !supermenu) ? 1 : 0,
|
|
1463
1728
|
translateY: sourceNodeShadow ? -this._sourceNodeDisplacement + 'px' : '0px',
|
|
1464
1729
|
translateX: sourceNodeShadow ? -this._sourceNodeHorizontalDisplacement + 'px' : '0px',
|
|
1465
1730
|
translateZ: 0
|
|
@@ -1659,7 +1924,7 @@ BMMenu.prototype = {
|
|
|
1659
1924
|
if (this._node) {
|
|
1660
1925
|
this.closeAnimated(YES);
|
|
1661
1926
|
}
|
|
1662
|
-
}
|
|
1927
|
+
},
|
|
1663
1928
|
|
|
1664
1929
|
|
|
1665
1930
|
};
|
|
@@ -1667,13 +1932,13 @@ BMMenu.prototype = {
|
|
|
1667
1932
|
/**
|
|
1668
1933
|
* Constructs and returns a menu with the specified items.
|
|
1669
1934
|
* The menu will be hidden by default; the method `openAtPoint(_)` should be used to make the menu visible.
|
|
1670
|
-
*
|
|
1935
|
+
*
|
|
1671
1936
|
* @param items <[BMMenuItem]> An array of menu items that will be displayed by this menu.
|
|
1672
|
-
*
|
|
1937
|
+
*
|
|
1673
1938
|
* @return <BMMenu> A menu.
|
|
1674
1939
|
*/
|
|
1675
1940
|
BMMenu.menuWithItems = function (items) {
|
|
1676
1941
|
return (new BMMenu).initWithItems(items);
|
|
1677
1942
|
};
|
|
1678
1943
|
|
|
1679
|
-
// @endtype
|
|
1944
|
+
// @endtype
|