bm-core-ui 2.9.5 → 2.10.1

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 (56) hide show
  1. package/build/@types/index.d.ts +320 -61
  2. package/build/BMCollectionView/BMCollectionView.js +79 -41
  3. package/build/BMCollectionView/BMCollectionViewFlowLayout.js +18 -5
  4. package/build/BMCoreUI.css +45 -18
  5. package/build/BMView/BMMenu.js +135 -7
  6. package/build/BMView/BMView_v2.5.js +59 -0
  7. package/build/BMWindow/BMKeyboardShortcut.js +338 -88
  8. package/build/BMWindow/BMPopover/BMPopover.js +52 -11
  9. package/build/BMWindow/BMWindow.js +32 -6
  10. package/build/Core/BMCoreUI.js +20 -8
  11. package/build/images/AlignBottom.png +0 -0
  12. package/build/images/AlignCenterX.png +0 -0
  13. package/build/images/AlignCenterY.png +0 -0
  14. package/build/images/AlignLeading.png +0 -0
  15. package/build/images/AlignTop.png +0 -0
  16. package/build/images/AlignTrailing.png +0 -0
  17. package/build/images/AllConstraints.png +0 -0
  18. package/build/images/BottomConstraint.png +0 -0
  19. package/build/images/CenterXConstraint.png +0 -0
  20. package/build/images/CenterYConstraint.png +0 -0
  21. package/build/images/CoreUI2.png +0 -0
  22. package/build/images/CoreUI2@2x.png +0 -0
  23. package/build/images/Desktop.png +0 -0
  24. package/build/images/DesktopMini.png +0 -0
  25. package/build/images/EqualHeight.png +0 -0
  26. package/build/images/EqualHorizontalSpacing.png +0 -0
  27. package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
  28. package/build/images/EqualVerticalSpacing.png +0 -0
  29. package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
  30. package/build/images/EqualWidth.png +0 -0
  31. package/build/images/HeightConstraint.png +0 -0
  32. package/build/images/InactiveConstraints.png +0 -0
  33. package/build/images/Layout.png +0 -0
  34. package/build/images/LayoutVariables.png +0 -0
  35. package/build/images/LeftConstraint.png +0 -0
  36. package/build/images/OwnConstraints.png +0 -0
  37. package/build/images/Phone.png +0 -0
  38. package/build/images/PhoneLandscape.png +0 -0
  39. package/build/images/PhoneLandscapeMini.png +0 -0
  40. package/build/images/PhoneMini.png +0 -0
  41. package/build/images/PhonePortrait.png +0 -0
  42. package/build/images/PhonePortraitMini.png +0 -0
  43. package/build/images/Properties.png +0 -0
  44. package/build/images/RightConstraint.png +0 -0
  45. package/build/images/SubviewConstraints.png +0 -0
  46. package/build/images/Tablet.png +0 -0
  47. package/build/images/TabletLandscape.png +0 -0
  48. package/build/images/TabletLandscapeMini.png +0 -0
  49. package/build/images/TabletMini.png +0 -0
  50. package/build/images/TabletPortrait.png +0 -0
  51. package/build/images/TabletPortraitMini.png +0 -0
  52. package/build/images/TopConstraint.png +0 -0
  53. package/build/images/WidthConstraint.png +0 -0
  54. package/lib/@types/BMCoreUI.min.d.ts +320 -61
  55. package/lib/BMCoreUI.min.js +3 -1
  56. package/package.json +9 -10
@@ -128,12 +128,72 @@ BMMenuItem.prototype = {
128
128
  this._CSSClass = cls || '';
129
129
  },
130
130
 
131
+ /**
132
+ * An optional key sequence that may be used to select this menu item via a keyboard shortcut.
133
+ * Updates to this property do not take effect while a menu displaying this menu item is open.
134
+ */
135
+ _keySequence: undefined, // <BMKeySequence, nullable>
136
+ get keySequence() {
137
+ return this._keySequence;
138
+ },
139
+ set keySequence(sequence) {
140
+ this._keySequence = sequence;
141
+ },
142
+
143
+ /**
144
+ * An optional hint HTML text that will be displayed next to the menu item.
145
+ */
146
+ _hintHTML: undefined, // <String, nullable>
147
+ get hintHTML() {
148
+ return this._hintHTML;
149
+ },
150
+ set hintHTML(hint) {
151
+ this._hintHTML = hint;
152
+ if (this._node) {
153
+ const hintContainer = this._node.querySelector('.BMMenuItemHint');
154
+ if (hintContainer) {
155
+ hintContainer.innerHTML = hint;
156
+ }
157
+ }
158
+ },
159
+
131
160
  /**
132
161
  * The node that represents this menu item, available while its menu
133
162
  * is open.
134
163
  */
135
164
  _node: undefined, // <DOMNode, nullable>
136
165
 
166
+ /**
167
+ * Defaults to `YES`. Controls whether this menu item is enabled. If this menu
168
+ * item is not enabled, it can not be highlighted or selected.
169
+ */
170
+ _enabled: YES, // <Boolean>
171
+ get enabled() {
172
+ return this._enabled;
173
+ },
174
+ set enabled(enabled) {
175
+ if (this._enabled == enabled) {
176
+ return;
177
+ }
178
+
179
+ // Separators can never be enabled
180
+ if (this._isSeparator) {
181
+ return;
182
+ }
183
+
184
+ this._enabled = enabled;
185
+
186
+ if (this._node) {
187
+ /** @type {HTMLElement} */ const node = this._node;
188
+ if (enabled) {
189
+ node.classList.remove('BMMenuItemDisabled');
190
+ }
191
+ else {
192
+ node.classList.add('BMMenuItemDisabled');
193
+ }
194
+ }
195
+ },
196
+
137
197
  /**
138
198
  * Constructs and returns a menu item with the given name.
139
199
  * Optionally, the menu item may have an icon associated with it as well as an
@@ -145,12 +205,13 @@ BMMenuItem.prototype = {
145
205
  *
146
206
  * @param action <void ^ (BMMenuItem), nullable> If specified, this is a function that will be invoked when this menu item is selected.
147
207
  * This function will receive the selected menu item as a parameter.
208
+ * @param enabled <Boolean, nullable> Defaults to `YES`. Controls whether this item is enabled and can be selected.
148
209
  * @param submenu <BMMenu, nullable> Optional. If specified, this represents a menu that opens from this menu item.
149
210
  * @param userInfo <AnyObject, nullable> Optional. If specified, this represents additional arbitrary data attached to this menu item.
150
211
  * }
151
212
  * @return <BMMenuItem> This menu item.
152
213
  */
153
- initWithName(name, {icon, action, submenu, userInfo} = {}) {
214
+ initWithName(name, {icon, action, submenu, enabled = true, userInfo} = {}) {
154
215
  this._name = name;
155
216
  this._icon = icon;
156
217
  this._action = action;
@@ -159,8 +220,11 @@ BMMenuItem.prototype = {
159
220
 
160
221
  if (name.startsWith('---')) {
161
222
  this._isSeparator = YES;
223
+ this._enabled = NO;
162
224
  }
163
225
 
226
+ this._enabled = enabled;
227
+
164
228
  return this;
165
229
  }
166
230
 
@@ -177,6 +241,7 @@ BMMenuItem.prototype = {
177
241
  *
178
242
  * @param action <void ^ (BMMenuItem), nullable> If specified, this is a function that will be invoked when this menu item is selected.
179
243
  * This function will receive the selected menu item as a parameter.
244
+ * @param enabled <Boolean, nullable> Defaults to `YES`. Controls whether this item is enabled and can be selected.
180
245
  * @param submenu <BMMenu, nullable> Optional. If specified, this represents a menu that opens from this menu item.
181
246
  * @param userInfo <AnyObject, nullable> Optional. If specified, this represents additional arbitrary data attached to this menu item.
182
247
  * }
@@ -225,7 +290,7 @@ BMMenu.prototype = {
225
290
  },
226
291
 
227
292
  /**
228
- * Defaults to 24. The size to use for this menu's icons.
293
+ * Defaults to `24`. The size to use for this menu's icons.
229
294
  */
230
295
  _iconSize: 24, // <Number>
231
296
  get iconSize() {
@@ -461,6 +526,15 @@ BMMenu.prototype = {
461
526
  escape.preventsDefault = YES;
462
527
  BMView.registerKeyboardShortcut(escape, {forNode: menuNode});
463
528
 
529
+ // Register a keyboard shortcut for each menu item that specifies one
530
+ for (const item of this._items) {
531
+ if (item.keySequence) {
532
+ const shortcut = BMKeyboardShortcut.keyboardShortcutWithKeySequence(item.keySequence, {target: this, action: '_selectMenuItemWithEvent'});
533
+ shortcut._menuItem = item;
534
+ BMView.registerKeyboardShortcut(shortcut, {forNode: menuNode});
535
+ }
536
+ }
537
+
464
538
  // The overlay which intercepts clicks outside of the menu
465
539
  const menuContainer = container || document.createElement('div');
466
540
  if (!container) {
@@ -560,6 +634,12 @@ BMMenu.prototype = {
560
634
  itemText.innerText = item.name;
561
635
  itemNode.appendChild(itemText);
562
636
 
637
+ // Create the hint container
638
+ const hint = document.createElement('div');
639
+ hint.classList.add('BMMenuItemHint');
640
+ hint.innerHTML = item._hintHTML || '';
641
+ itemNode.appendChild(hint);
642
+
563
643
  // Create the disclosure container
564
644
  const submenuIcon = document.createElement('div');
565
645
  submenuIcon.classList.add('BMMenuItemDisclosure');
@@ -568,9 +648,18 @@ BMMenu.prototype = {
568
648
  submenuIcon.classList.add('BMMenuItemDisclosureSubmenu');
569
649
  }
570
650
  itemNode.appendChild(submenuIcon);
651
+
652
+ if (!item._enabled) {
653
+ itemNode.classList.add('BMMenuItemDisabled');
654
+ }
571
655
 
572
656
  // TWhen clicking an item, trigger its action, if it has one
573
657
  itemNode.addEventListener('click', event => {
658
+ // Disabled items can't be clicked
659
+ if (!item._enabled) {
660
+ return;
661
+ }
662
+
574
663
  if (item.submenu && this._sourceNodeShadow) {
575
664
  // For touch menus, if the item has a submenu, open it
576
665
  this._openSubmenuForMenuItem(item, {animated: YES, delayEvents: NO, acquireFocus: YES});
@@ -611,6 +700,12 @@ BMMenu.prototype = {
611
700
  // When moving the mouse over an item, set the highlight index to it,
612
701
  // allowing further keyboard navigation to continute from this item
613
702
  itemNode.addEventListener('mouseover', event => {
703
+ // Disabled items can't be highlighted and can't open submenus
704
+ if (!item._enabled) {
705
+ this.highlightedIndex = -1;
706
+ return;
707
+ }
708
+
614
709
  if (this._highlightedIndex == index) return;
615
710
 
616
711
  // If events should be delayed, don't process this mouseover event
@@ -642,7 +737,8 @@ BMMenu.prototype = {
642
737
  _kind: BMMenuKind.Menu,
643
738
 
644
739
  /**
645
- * Animatable. Shows this menu from the given DOM node.
740
+ * Animatable. Shows this menu from the specified DOM node. The menu will be displayed as a touch menu,
741
+ * highlighting the source node and de-emphasizing the rest of the document.
646
742
  * @param node <DOMNode> The node from which to show this menu.
647
743
  * {
648
744
  * @param animated <Boolean, nullable> Defaults to `NO`. If set to `YES`, this change will be animated.
@@ -1315,8 +1411,8 @@ BMMenu.prototype = {
1315
1411
  while (YES) {
1316
1412
  previousIndex = BMNumberByConstrainingNumberToBounds(previousIndex - 1, 0, this._items.length - 1);
1317
1413
 
1318
- // If item at the previous index isn't a separator, highlight it
1319
- if (!this._items[previousIndex]._isSeparator) break;
1414
+ // If item at the previous index isn't a separator or disabled, highlight it
1415
+ if (!this._items[previousIndex]._isSeparator && this._items[previousIndex]._enabled) break;
1320
1416
 
1321
1417
  // If the start of the items array has been reached and a viable highlightable item hasn't
1322
1418
  // been found, don't take any action
@@ -1337,8 +1433,8 @@ BMMenu.prototype = {
1337
1433
  while (YES) {
1338
1434
  nextIndex = BMNumberByConstrainingNumberToBounds(nextIndex + 1, 0, this._items.length - 1);
1339
1435
 
1340
- // If item at the next index isn't a separator, highlight it
1341
- if (!this._items[nextIndex]._isSeparator) break;
1436
+ // If item at the next index isn't a separator or disabled, highlight it
1437
+ if (!this._items[nextIndex]._isSeparator && this._items[nextIndex]._enabled) break;
1342
1438
 
1343
1439
  // If the end of the items array has been reached and a viable highlightable item hasn't
1344
1440
  // been found, don't take any action
@@ -1375,6 +1471,38 @@ BMMenu.prototype = {
1375
1471
  }
1376
1472
  },
1377
1473
 
1474
+ /**
1475
+ * Invoked when the key sequence for a menu item is pressed.
1476
+ * @param event <KeyboardEvent> The event that triggered this action.
1477
+ * {
1478
+ * @param forKeyboardShortcut <BMKeyboardShortcut> The keyboard shortcut that was triggered.
1479
+ * }
1480
+ */
1481
+ _selectMenuItemWithEvent(event, {forKeyboardShortcut: shortcut}) {
1482
+ event.preventDefault();
1483
+ const item = shortcut._menuItem;
1484
+
1485
+ // The item must not be a separator or disabled
1486
+ if (item._isSeparator || !item._enabled) {
1487
+ return;
1488
+ }
1489
+
1490
+ if (!this._node) {
1491
+ return;
1492
+ }
1493
+
1494
+ this._node.inert = true;
1495
+ this._node.style.pointerEvents = 'none';
1496
+
1497
+ // Otherwise highlight the item then send a return key to select it
1498
+ const index = this._items.indexOf(item);
1499
+ this.highlightedIndex = index;
1500
+
1501
+ setTimeout(() => {
1502
+ this.returnPressedWithEvent(event);
1503
+ }, 50);
1504
+ },
1505
+
1378
1506
  /**
1379
1507
  * Invoked when the return or spacebar key is pressed.
1380
1508
  * @param event <KeyboardEvent> The event that triggered this action.
@@ -261,6 +261,32 @@ BMViewLayoutQueue.layoutQueue = function () {
261
261
 
262
262
  // @endtype
263
263
 
264
+ // @type BMViewColorScheme
265
+
266
+ /**
267
+ * Constants that describe the color scheme that a view should use when drawing itself.
268
+ */
269
+ export var BMViewColorScheme = Object.freeze({ // <enum>
270
+
271
+ /**
272
+ * Indicates that the view's color scheme should be based on the system color scheme.
273
+ */
274
+ Auto: 'auto', // <enum>
275
+
276
+ /**
277
+ * Indicates that the view should always use a dark color scheme regardless of the system color scheme setting.
278
+ */
279
+ Dark: 'dark', // <enum>
280
+
281
+ /**
282
+ * Indicates that the view should always use a light color scheme regardless of the system color scheme setting.
283
+ */
284
+ Light: 'light', // <enum>
285
+
286
+ });
287
+
288
+ // @endtype
289
+
264
290
  // @type BMView
265
291
 
266
292
  const BMViewDebug = NO;
@@ -1784,6 +1810,39 @@ BMView.prototype = BMExtend(BMView.prototype, {
1784
1810
 
1785
1811
  // #endregion
1786
1812
 
1813
+ // #region Color Scheme
1814
+
1815
+ /**
1816
+ * The color scheme that this view should use.
1817
+ */
1818
+ _colorScheme: BMViewColorScheme.Auto, // <BMViewColorScheme>
1819
+
1820
+ get colorScheme() {
1821
+ return this._colorScheme;
1822
+ },
1823
+
1824
+ set colorScheme(scheme) {
1825
+ const oldColorScheme = this._colorScheme;
1826
+ this._colorScheme = scheme;
1827
+
1828
+ this.colorSchemeDidChange(oldColorScheme);
1829
+ },
1830
+
1831
+ /**
1832
+ * @protected
1833
+ * Invoked by CoreUI whenever the color scheme that the view should use changes.
1834
+ *
1835
+ * Subclasses may use this method to perform any changes needed to support the requested color scheme.
1836
+ * Subclasses that override this method must invoke the superclass method at some point in their implementation.
1837
+ *
1838
+ * @param scheme <BMViewColorScheme> The previous color scheme.
1839
+ */
1840
+ colorSchemeDidChange(scheme) {
1841
+
1842
+ },
1843
+
1844
+ // #endregion
1845
+
1787
1846
  // #region Intrinsic Size
1788
1847
 
1789
1848
  /**