bm-core-ui 2.7.0

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 (99) hide show
  1. package/CHANGELOG.md +1624 -0
  2. package/LICENSE +21 -0
  3. package/README.md +145 -0
  4. package/build/@types/index.d.ts +14078 -0
  5. package/build/BMCodeEditor/BMCodeEditor.js +707 -0
  6. package/build/BMCollectionView/BMCollectionView.js +5861 -0
  7. package/build/BMCollectionView/BMCollectionViewCell.js +688 -0
  8. package/build/BMCollectionView/BMCollectionViewFlowLayout.js +4467 -0
  9. package/build/BMCollectionView/BMCollectionViewLayout.js +830 -0
  10. package/build/BMCollectionView/BMCollectionViewLayoutAttributes.js +673 -0
  11. package/build/BMCollectionView/BMCollectionViewMasonryLayout.js +491 -0
  12. package/build/BMCollectionView/BMCollectionViewStackLayout.js +634 -0
  13. package/build/BMCollectionView/BMCollectionViewTileLayout.js +1121 -0
  14. package/build/BMCoreUI.css +2833 -0
  15. package/build/BMView/BMAttributedLabelView.js +310 -0
  16. package/build/BMView/BMLayoutConstraint_v2.5.js +1813 -0
  17. package/build/BMView/BMLayoutGuide.js +192 -0
  18. package/build/BMView/BMLayoutSizeClass.js +466 -0
  19. package/build/BMView/BMMenu.js +574 -0
  20. package/build/BMView/BMScrollView.js +247 -0
  21. package/build/BMView/BMTextField.js +512 -0
  22. package/build/BMView/BMTextFieldDelegate.js +71 -0
  23. package/build/BMView/BMView_v2.5.js +3572 -0
  24. package/build/BMView/BMViewport.js +221 -0
  25. package/build/BMViewLayoutEditor/BMLayoutEditor.js +6117 -0
  26. package/build/BMViewLayoutEditor/BMLayoutEditorConstraintSettings.js +417 -0
  27. package/build/BMViewLayoutEditor/BMLayoutEditorDelegate.js +59 -0
  28. package/build/BMViewLayoutEditor/BMLayoutEditorSettingCells.js +1653 -0
  29. package/build/BMViewLayoutEditor/BMLayoutEditorSettings.js +1480 -0
  30. package/build/BMViewLayoutEditor/BMLayoutEditorSettingsComplexCells.js +431 -0
  31. package/build/BMViewLayoutEditor/BMLayoutEditorSettingsDelegate.js +46 -0
  32. package/build/BMViewLayoutEditor/BMLayoutEditorVariablesController.js +460 -0
  33. package/build/BMViewLayoutEditor/BMLayoutEditorViewGroupSettings.js +293 -0
  34. package/build/BMViewLayoutEditor/BMLayoutEditorViewSettings.js +382 -0
  35. package/build/BMViewLayoutEditor/BMLayoutVariableProvider.js +202 -0
  36. package/build/BMWindow/BMConfirmationPopup.js +477 -0
  37. package/build/BMWindow/BMKeyboardShortcut.js +151 -0
  38. package/build/BMWindow/BMPopover/BMPopover.js +492 -0
  39. package/build/BMWindow/BMToolWindow.js +71 -0
  40. package/build/BMWindow/BMWindow.js +2000 -0
  41. package/build/Core/BMAnimationContext.js +1181 -0
  42. package/build/Core/BMColor.js +991 -0
  43. package/build/Core/BMCoreUI.js +470 -0
  44. package/build/Core/BMFunctionCollection.js +110 -0
  45. package/build/Core/BMIndexPath.js +165 -0
  46. package/build/Core/BMInset.js +138 -0
  47. package/build/Core/BMKeyPath.js +100 -0
  48. package/build/Core/BMPoint.js +291 -0
  49. package/build/Core/BMRect.js +556 -0
  50. package/build/Core/BMSize.js +137 -0
  51. package/build/iScroll/LICENSE +22 -0
  52. package/build/iScroll/iscroll-probe.js +2154 -0
  53. package/build/images/AlignBottom.png +0 -0
  54. package/build/images/AlignCenterX.png +0 -0
  55. package/build/images/AlignCenterY.png +0 -0
  56. package/build/images/AlignLeading.png +0 -0
  57. package/build/images/AlignTop.png +0 -0
  58. package/build/images/AlignTrailing.png +0 -0
  59. package/build/images/AllConstraints.png +0 -0
  60. package/build/images/BottomConstraint.png +0 -0
  61. package/build/images/CenterXConstraint.png +0 -0
  62. package/build/images/CenterYConstraint.png +0 -0
  63. package/build/images/CoreUI2.png +0 -0
  64. package/build/images/CoreUI2@2x.png +0 -0
  65. package/build/images/Desktop.png +0 -0
  66. package/build/images/DesktopMini.png +0 -0
  67. package/build/images/EqualHeight.png +0 -0
  68. package/build/images/EqualHorizontalSpacing.png +0 -0
  69. package/build/images/EqualHorizontalSpacingInSuperview.png +0 -0
  70. package/build/images/EqualVerticalSpacing.png +0 -0
  71. package/build/images/EqualVerticalSpacingInSuperview.png +0 -0
  72. package/build/images/EqualWidth.png +0 -0
  73. package/build/images/HeightConstraint.png +0 -0
  74. package/build/images/InactiveConstraints.png +0 -0
  75. package/build/images/Layout.png +0 -0
  76. package/build/images/LayoutVariables.png +0 -0
  77. package/build/images/LeftConstraint.png +0 -0
  78. package/build/images/OwnConstraints.png +0 -0
  79. package/build/images/Phone.png +0 -0
  80. package/build/images/PhoneLandscape.png +0 -0
  81. package/build/images/PhoneLandscapeMini.png +0 -0
  82. package/build/images/PhoneMini.png +0 -0
  83. package/build/images/PhonePortrait.png +0 -0
  84. package/build/images/PhonePortraitMini.png +0 -0
  85. package/build/images/Properties.png +0 -0
  86. package/build/images/RightConstraint.png +0 -0
  87. package/build/images/SubviewConstraints.png +0 -0
  88. package/build/images/Tablet.png +0 -0
  89. package/build/images/TabletLandscape.png +0 -0
  90. package/build/images/TabletLandscapeMini.png +0 -0
  91. package/build/images/TabletMini.png +0 -0
  92. package/build/images/TabletPortrait.png +0 -0
  93. package/build/images/TabletPortraitMini.png +0 -0
  94. package/build/images/TopConstraint.png +0 -0
  95. package/build/images/WidthConstraint.png +0 -0
  96. package/build/index.js +40 -0
  97. package/lib/@types/BMCoreUI.min.d.ts +14078 -0
  98. package/lib/BMCoreUI.min.js +1 -0
  99. package/package.json +58 -0
@@ -0,0 +1,151 @@
1
+ import { NO } from "../Core/BMCoreUI";
2
+
3
+ // @ts-check
4
+
5
+ // @type BMKeyboardShortcutModifier
6
+
7
+ /**
8
+ * Contains constants representing the modifier keys that can be used when registering keyboard shortcuts
9
+ * for views and windows.
10
+ */
11
+ export const BMKeyboardShortcutModifier = Object.freeze({ // <enum>
12
+ /**
13
+ * Represents the command key on macOS and iOS, windows key on Windows and meta key on Linux.
14
+ */
15
+ Command: {key: 'metaKey', value: 1}, // <enum>
16
+ /**
17
+ * Represents the option key on macOS and alt on other systems.
18
+ */
19
+ Option: {key: 'altKey', value: 2}, // <enum>
20
+ /**
21
+ * Represents the shit key.
22
+ */
23
+ Shift: {key: 'shiftKey', value: 4}, // <enum>
24
+ /**
25
+ * Represents the control key.
26
+ */
27
+ Control: {key: 'ctrlKey', value: 8}, // <enum>
28
+ /**
29
+ * Represents the command key on macOS and iOS and control key on other systems.
30
+ */
31
+ System: {key: 'systemKey', value: (navigator.platform.startsWith('Mac') || /iPhone|iPad|iPod/.test(navigator.platform)) ? 1 : 8}, // <enum>
32
+
33
+ });
34
+
35
+ // @endtype
36
+
37
+ // @type BMKeyboardShortcut
38
+
39
+ /**
40
+ * A keyboard shortcut object describes the keys that must be pressed for a keyboard shortcut to trigger as
41
+ * well as what should happen when that shortcut is triggered.
42
+ */
43
+ export function BMKeyboardShortcut() {} // <constructor>
44
+
45
+ BMKeyboardShortcut.prototype = {
46
+
47
+ /**
48
+ * The key that should be pressed for this keyboard shortcut.
49
+ */
50
+ _keyCode: undefined, // <String>
51
+ get keyCode() {
52
+ return this._key;
53
+ },
54
+
55
+ /**
56
+ * An optional array of keyboard modifiers that must be pressed
57
+ * together with the key in order to trigger the shortcut.
58
+ */
59
+ _modifiers: undefined, // <[BMKeyboardShortcutModifier]>
60
+ get modifiers() {
61
+ if (this._modifiers) return this._modifiers.slice();
62
+ },
63
+
64
+ /**
65
+ * A bitmap describing the modifiers that should be active.
66
+ */
67
+ _modifierBitmap: 0, // <Number>
68
+
69
+ /**
70
+ * The object which will handle this keyboard shortcut.
71
+ */
72
+ _target: undefined, // <AnyObject>
73
+ get target() {
74
+ return this._target;
75
+ },
76
+ set target(target) {
77
+ this._target = target;
78
+ },
79
+
80
+ /**
81
+ * The name of the method that will be invoked on the target object when this keyboard
82
+ * shortcut is triggered. This method will receive the keyboard event that triggered
83
+ * this action as its single parameter.
84
+ */
85
+ _action: undefined, // <String>
86
+ get action() {
87
+ return this._action;
88
+ },
89
+ set action(action) {
90
+ this._action = action;
91
+ },
92
+
93
+ /**
94
+ * When set to `YES`, the default action of the event that triggers this keyboard shortcut will be prevented.
95
+ */
96
+ preventsDefault: NO, // <Boolean>
97
+
98
+ /**
99
+ * Initializes this keyboard shortcut with the given key and optional modifiers, as well as
100
+ * the target and action that will handle it.
101
+ *
102
+ * In order to be triggered, this keyboard shortcut must be registered with an event handler such as a window
103
+ * that can receive keyboard focus and keypress events.
104
+ * @param key <String> The key that should be pressed for this keyboard shortcut. This maps to the `code` property of keyboard events.
105
+ * {
106
+ * @param modifiers <[BMKeyboardShortcutModifier], nullable> Defaults to an empty array. An optional array of keyboard modifiers that must be active.
107
+ * for the keyboard shortcut to fire.
108
+ * @param target <AnyObject> The object that will handle this keyboard shortcut action.
109
+ * @param action <String> The name of a method on the target object that will be invoked when this
110
+ * keyboard shortcut is triggered. That method will receive the keyboard event as its single parameter.
111
+ * @param preventsDefault <Boolean, nullable> Defaults to `NO`. When set to `YES`, the default action of the event that triggers this keyboard shortcut will be prevented.
112
+ * }
113
+ * @return <BMKeyboardShortcut> This keyboard shortcut.
114
+ */
115
+ initWithKeyCode(key, {modifiers, target, action, preventsDefault} = {modifiers: []}) {
116
+ this._key = key;
117
+ this._modifiers = modifiers || [];
118
+ this._target = target;
119
+ this._action = action;
120
+ this.preventsDefault = preventsDefault || NO;
121
+
122
+ for (const modifier of this._modifiers) {
123
+ this._modifierBitmap = this._modifierBitmap | modifier.value;
124
+ }
125
+
126
+ return this;
127
+ }
128
+
129
+ };
130
+
131
+ /**
132
+ * Constructs and returns a keyboard shortcut with the given key and optional modifiers, as well as
133
+ * the target and action that will handle it.
134
+ *
135
+ * In order to be triggered, this keyboard shortcut must be registered with an event handler such as a window
136
+ * that can receive keyboard focus and keypress events.
137
+ * @param key <String> The key that should be pressed for this keyboard shortcut. This maps to the `code` property of keyboard events.
138
+ * {
139
+ * @param modifiers <[BMKeyboardShortcutModifier], nullable> Defaults to an empty array. An optional array of keyboard modifiers that must be active.
140
+ * for the keyboard shortcut to fire.
141
+ * @param target <AnyObject> The object that will handle this keyboard shortcut action.
142
+ * @param action <String> The name of a method on the target object that will be invoked when this
143
+ * keyboard shortcut is triggered. That method will receive the keyboard event as its single parameter.
144
+ * }
145
+ * @return <BMKeyboardShortcut> A keyboard shortcut.
146
+ */
147
+ BMKeyboardShortcut.keyboardShortcutWithKeyCode = function (key, args) {
148
+ return (new BMKeyboardShortcut).initWithKeyCode(key, args);
149
+ };
150
+
151
+ // @endtype
@@ -0,0 +1,492 @@
1
+ // @ts-check
2
+
3
+ import { BMExtend, NO, YES, BMCopyProperties, BMNumberByConstrainingNumberToBounds, BMUUIDMake } from "../../Core/BMCoreUI";
4
+ import { BMWindow } from "../BMWindow";
5
+ import { BMRectMakeWithOrigin, BMRectMakeWithNodeFrame, BMRectMake } from "../../Core/BMRect";
6
+ import { BMPointMake } from "../../Core/BMPoint";
7
+ import { BMHook, __BMVelocityAnimate } from "../../Core/BMAnimationContext";
8
+ import { BMView } from "../../BMView/BMView_v2.5";
9
+ import { BMInsetMakeWithEqualInsets } from "../../Core/BMInset";
10
+
11
+ // @type BMPopover extends BMWindow
12
+
13
+ /**
14
+ * The popover is a kind of window that is visually linked to a source element.
15
+ * Unlike windows, popovers are always modal.
16
+ *
17
+ * Additionally, unlike with windows, subviews should not be directly added to the popover.
18
+ * Instead, they should be added to the popover's `contentView` property.
19
+ */
20
+ function BMPopover() {} // <constructor>
21
+
22
+ BMPopover.prototype = BMExtend(Object.create(BMWindow.prototype), {
23
+
24
+ /**
25
+ * The point from which this popover should originate, relative to the document. Either this property
26
+ * or `anchorNode` or `anchorRect` must be set before this popover is displayed.
27
+ */
28
+ _anchorPoint: undefined, // <BMPoint, nullable>
29
+
30
+ get anchorPoint() {
31
+ return this._anchorPoint;
32
+ },
33
+ set anchorPoint(point) {
34
+ this._anchorPoint = point;
35
+ },
36
+
37
+
38
+ /**
39
+ * The rect from which this popover should originate, relative to the document. Either this property
40
+ * or `anchorPoint` or `anchorNode` must be set before this popover is displayed.
41
+ */
42
+ _anchorRect: undefined, // <BMRect, nullable>
43
+
44
+ get anchorRect() {
45
+ return this._anchorRect;
46
+ },
47
+ set anchorRect(rect) {
48
+ this._anchorRect = rect;
49
+ },
50
+
51
+
52
+ /**
53
+ * The element from which this popover should originate. Either this property
54
+ * or `anchorPoint` or `anchorRect` must be set before this popover is displayed.
55
+ */
56
+ _anchorNode: undefined, // <DOMNode, nullable>
57
+
58
+ get anchorNode() {
59
+ return this._anchorNode;
60
+ },
61
+ set anchorNode(node) {
62
+ this._anchorNode = node;
63
+ },
64
+
65
+ /**
66
+ * This popover's size. This property must be set prior to the
67
+ * popover being displayed.
68
+ */
69
+ _size: undefined, // <BMSize>
70
+ get size() {
71
+ return this._size;
72
+ },
73
+ set size(size) {
74
+ this._size = size.copy();
75
+ },
76
+
77
+ /**
78
+ * The size of the indicator. This property should be set prior to the
79
+ * popover being displayed.
80
+ */
81
+ _indicatorSize: 16, // <Number>
82
+ get indicatorSize() {
83
+ return this._indicatorSize;
84
+ },
85
+ set indicatorSize(size) {
86
+ this._indicatorSize = size;
87
+ },
88
+
89
+ /**
90
+ * Controls how rounded the popover's borders should be. This property should be set prior to the
91
+ * popover being displayed.
92
+ */
93
+ _borderRadius: 4, // <Number>
94
+ get borderRadius() {
95
+ return this._borderRadius;
96
+ },
97
+ set borderRadius(radius) {
98
+ this._borderRadius = radius;
99
+ },
100
+
101
+ /**
102
+ * Controls the spacing that this popover will maintain towards the edges of the viewport
103
+ * in cases where the popover would move outside the visible area in order to maintain its
104
+ * regular position.
105
+ */
106
+ _edgeInsets: undefined, // <BMInset>
107
+ get edgeInsets() {
108
+ return this._edgeInsets;
109
+ },
110
+ set edgeInsets(margin) {
111
+ this._edgeInsets = margin || BMInsetMakeWithEqualInsets(8);
112
+
113
+ if (this.isVisible) {
114
+ // If this is updated while the popover is visible, update its position accordingly
115
+ // TODO: Only update when this would actually change the position
116
+ this._updatePosition();
117
+ }
118
+ },
119
+
120
+ /**
121
+ * The drop shadow container.
122
+ */
123
+ _dropShadowContainer: undefined, // <DOMNode>
124
+
125
+ /**
126
+ * The drop shadow content.
127
+ */
128
+ _dropShadowContent: undefined, // <DOMNode>
129
+
130
+ /**
131
+ * The background node.
132
+ */
133
+ _background: undefined, // <DOMNode>
134
+
135
+ /**
136
+ * The dark mode fill node.
137
+ */
138
+ _darkModeFill: undefined, // <DOMNode>
139
+
140
+ /**
141
+ * The view to which subviews should be added.
142
+ */
143
+ _contentView: undefined, // <BMView>
144
+ get contentView() {
145
+ return this._contentView;
146
+ },
147
+ set contentView(view) {
148
+ this._contentView = view;
149
+ },
150
+
151
+ /**
152
+ * A SVG object that defines the clip path that must be used on
153
+ * browsers that don't suport the `clip-path: path(...)` CSS values.
154
+ * This property will be `undefined` for all other browsers.
155
+ */
156
+ _clipPathSVG: undefined, // <DOMNode, nullable>
157
+
158
+ /**
159
+ * When `_clipPathSVG` is defined, this represents the background path element.
160
+ */
161
+ _clipPathBackgroundPath: undefined, // <DOMNode, nullable>
162
+
163
+ /**
164
+ * When `_clipPathSVG` is defined, this represents the outline path element.
165
+ */
166
+ _clipPathOutlinePath: undefined, // <DOMNode, nullable>
167
+
168
+ /**
169
+ * A string that forms part of the HTML IDs that will be assigned to the clip paths
170
+ * to be used on browsers.
171
+ * This property will be `undefined` for all other browsers.
172
+ */
173
+ _clipPathUUID: undefined, // <String, nullable>
174
+
175
+ /**
176
+ * The content view's top edge constraint.
177
+ */
178
+ _contentViewTopConstraint: undefined, // <BMLayoutConstraint>
179
+
180
+ /**
181
+ * The content view's bottom edge constraint.
182
+ */
183
+ _contentViewBottomConstraint: undefined, // <BMLayoutConstraint>
184
+
185
+ /**
186
+ * The computed height of the indicator.
187
+ */
188
+ get _indicatorHeight() { // <Number>
189
+ const indicatorWidth = this.indicatorSize * Math.SQRT2;
190
+ return indicatorWidth / 2 | 0;
191
+ },
192
+
193
+ /**
194
+ * Designated initializer. Initializes this popover with the given size.
195
+ *
196
+ * Note that the size will also contain the indicator size, so the actual usable size will be smaller,
197
+ * depending on how large the indicator is.
198
+ * @param size <BMSize> The popover's size.
199
+ * @return <BMPopover> This popover.
200
+ */
201
+ initWithSize(size) {
202
+ const preliminaryFrame = BMRectMakeWithOrigin(BMPointMake(), {size});
203
+ BMWindow.prototype.initWithFrame.call(this, preliminaryFrame, {modal: YES, toolbar: NO});
204
+
205
+ this._edgeInsets = BMInsetMakeWithEqualInsets(8);
206
+
207
+ this._size = size.copy();
208
+
209
+ // Set up the popover container
210
+ this.node.className = 'BMPopover';
211
+ this.contentNode.className = 'BMPopoverContainer BMPopoverContainerView';
212
+
213
+ this._overlay.node.className = 'BMPopoverOverlay';
214
+
215
+ // A second layer is used to draw the drop shadow, due to the unusual shape of the popover window
216
+ const popoverDropShadowContainer = document.createElement('div');
217
+ popoverDropShadowContainer.className = 'BMPopoverContainerLayer BMPopoverDropShadowContainer';
218
+ const popoverDropShadowContent = document.createElement('div');
219
+ popoverDropShadowContent.className = 'BMPopoverDropShadowContent';
220
+ popoverDropShadowContainer.appendChild(popoverDropShadowContent);
221
+
222
+ // A third layer is used to draw the background
223
+ const popoverBackground = document.createElement('div');
224
+ popoverBackground.className = 'BMPopoverBackground';
225
+
226
+ // Two additional sublayers are used for dark mode and contain the outline and background color.
227
+ popoverBackground.innerHTML = '<div class="BMPopoverBackgroundDarkModeContainer"><div class="BMPopoverBackgroundDarkModeOutline"></div><div class="BMPopoverBackgroundDarkModeFill"></div></div>';
228
+
229
+ // Add the popover to the document
230
+ this.node.appendChild(popoverBackground);
231
+ this.node.appendChild(popoverDropShadowContainer);
232
+ this.node.appendChild(this.contentNode);
233
+
234
+ this._background = popoverBackground;
235
+ this._dropShadowContainer = popoverDropShadowContainer;
236
+ this._dropShadowContent = popoverDropShadowContent;
237
+ this._popoverDarkModeFill = this._background.querySelector('.BMPopoverBackgroundDarkModeFill');
238
+
239
+ const contentView = this._contentView = BMView.view();
240
+ this.addSubview(this._contentView);
241
+
242
+ contentView.leading.equalTo(this.leading).isActive = YES;
243
+ contentView.trailing.equalTo(this.trailing).isActive = YES;
244
+ this._contentViewTopConstraint = contentView.top.equalTo(this.top);
245
+ this._contentViewTopConstraint.isActive = YES;
246
+
247
+ this._contentViewBottomConstraint = contentView.bottom.equalTo(this.bottom);
248
+ this._contentViewBottomConstraint.isActive = YES;
249
+
250
+ return this;
251
+ },
252
+
253
+ // @override - BMWindow
254
+ initWithFrame(frame) {
255
+ return this.initWithSize(frame.size);
256
+ },
257
+
258
+ /**
259
+ * Invoked by CoreUI to update this popover's position and recalculate the various paths used by it.
260
+ */
261
+ _updatePosition() {
262
+ const frame = BMRectMake();
263
+ frame.size.height = this._size.height + this._indicatorHeight;
264
+ frame.size.width = this._size.width;
265
+
266
+ const nodeFrame = this.anchorRect || (this.anchorNode && BMRectMakeWithNodeFrame(this.anchorNode));
267
+ const location = this.anchorPoint ? this.anchorPoint.copy() : nodeFrame.center;
268
+
269
+ frame.origin.x = location.x - frame.size.width / 2 | 0;
270
+
271
+ if (frame.origin.x < this._edgeInsets.left) {
272
+ frame.origin.x = this._edgeInsets.left;
273
+ }
274
+ if (frame.right > window.innerWidth - this._edgeInsets.right) {
275
+ frame.origin.x = window.innerWidth - frame.size.width - this._edgeInsets.right;
276
+ }
277
+
278
+ const appearsBelow = location.y < window.innerHeight - this.size.height - this._indicatorHeight;
279
+
280
+ if (appearsBelow) {
281
+ frame.origin.y = this.anchorPoint ? location.y : nodeFrame.bottom - 2;
282
+ this._contentViewTopConstraint.constant = this._indicatorHeight;
283
+ this._contentViewBottomConstraint.constant = 0;
284
+ }
285
+ else {
286
+ frame.origin.y = this.anchorPoint ? location.y - frame.size.height : nodeFrame.origin.y + 2 - frame.size.height;
287
+ this._contentViewTopConstraint.constant = 0;
288
+ this._contentViewBottomConstraint.constant = -this._indicatorHeight;
289
+ }
290
+
291
+ this.frame = frame;
292
+
293
+ const innerFrame = frame.copy();
294
+
295
+ const knobPosition = BMNumberByConstrainingNumberToBounds(location.x - frame.origin.x, 12, frame.size.width - 12);
296
+
297
+ innerFrame.origin = BMPointMake();
298
+
299
+ const pathContent = `${this._pathForPopoverWithFrame(innerFrame, {widthIndicatorSize: this._indicatorSize, knobPosition, gravity: appearsBelow ? 'Top' : 'Bottom'})}`;
300
+ const outlinePathContent = `${this._pathForPopoverWithFrame(innerFrame, {widthIndicatorSize: this._indicatorSize, inset: 1, knobPosition, gravity: appearsBelow ? 'Top' : 'Bottom'})}`;
301
+
302
+ if (!this._clipPathUUID && !CSS.supports('clip-path', `path('${pathContent}')`)) {
303
+ // If inline path definitions are not supported by the browsers, create an UUID for a SVG clip path and create it
304
+ this._clipPathUUID = BMUUIDMake();
305
+
306
+ this._clipPathSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
307
+ this._clipPathSVG.style.width = '0';
308
+ this._clipPathSVG.style.height = '0';
309
+
310
+ // Create and attach the main clip path
311
+ const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
312
+ clipPath.setAttribute('id', 'popover-clip-path-' + this._clipPathUUID);
313
+ clipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
314
+ this._clipPathBackgroundPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
315
+ clipPath.appendChild(this._clipPathBackgroundPath);
316
+
317
+ this._clipPathSVG.appendChild(clipPath);
318
+
319
+ // Create and attach the outline clip path
320
+ const outlineClipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
321
+ outlineClipPath.setAttribute('id', 'popover-outline-clip-path-' + this._clipPathUUID);
322
+ outlineClipPath.setAttribute('clipPathUnits', 'userSpaceOnUse');
323
+ this._clipPathOutlinePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
324
+ outlineClipPath.appendChild(this._clipPathOutlinePath);
325
+
326
+ this._clipPathSVG.appendChild(outlineClipPath);
327
+
328
+ document.body.appendChild(this._clipPathSVG);
329
+
330
+ }
331
+
332
+ // For Chrome, update the path nodes
333
+ if (this._clipPathUUID) {
334
+ this._clipPathBackgroundPath.setAttribute('d', pathContent);
335
+ this._clipPathOutlinePath.setAttribute('d', outlinePathContent);
336
+ }
337
+
338
+ // For Blink/Chrome-based browsers clip-path: path() is not supported, but clip-path: url() can be used instead for that browser
339
+ const path = this._clipPathUUID ? `url(#popover-clip-path-${this._clipPathUUID})` : `path('${pathContent}')`;
340
+ const outlinePath = this._clipPathUUID ? `url(#popover--outline-clip-path-${this._clipPathUUID})` : `path('${outlinePathContent}')`;
341
+
342
+ // Assign the frame to the window, and to the drop shadow container
343
+ const positionStyle = {
344
+ left: innerFrame.origin.x + 'px',
345
+ top: innerFrame.origin.y + 'px',
346
+ width: innerFrame.size.width + 'px',
347
+ height: innerFrame.size.height + 'px'
348
+ };
349
+ BMCopyProperties(this.contentNode.style, positionStyle);
350
+
351
+ BMCopyProperties(this._background.style, positionStyle);
352
+ this._background.style.clipPath = path;
353
+ this._background.style.webkitClipPath = path;
354
+
355
+ const popoverDarkModeFill = this._background.querySelector('.BMPopoverBackgroundDarkModeFill');
356
+ popoverDarkModeFill.style.clipPath = outlinePath;
357
+ popoverDarkModeFill.style.webkitClipPath = outlinePath;
358
+
359
+ BMCopyProperties(this._dropShadowContainer.style, positionStyle);
360
+ this._dropShadowContent.style.clipPath = path;
361
+ this._dropShadowContent.style.webkitClipPath = path;
362
+
363
+ const popoverLayers = [this.contentNode, this._background, this._dropShadowContainer];
364
+
365
+ for (const layer of popoverLayers) {
366
+ layer.style.transformOrigin = ((knobPosition / this.frame.size.width) * 100) + '% ' + (appearsBelow ? '0%' : '100%');
367
+ }
368
+ },
369
+
370
+ /**
371
+ * Builds the SVG path definition for a popover with the given frame. Note that the indicator will be positioned inside the frame, which will push the usable
372
+ * area of the frame downwards.
373
+ * @param frame <BMRect> The popover's frame.
374
+ * {
375
+ * @param widthIndicatorSize <Number, nullable> Defaults to `8`. The size of the popover's indicator.
376
+ * @param radius <Number, nullable> Defaults to `4`. Controls how rounded the corners are.
377
+ * @param inset <Number, nullable> Defaults to `0`. An optional inset to apply to the path.
378
+ * @param knobPosition <Number, nullable> Defaults to half of the frame's width. The position along the top frame on which to place the knob.
379
+ * This coordinate is relative to the popover's frame and represents the center position of the knob.
380
+ * This position should not overlap the specified corner radius.
381
+ * @param gravity <String, nullable> Defaults to `"Top"`. If set to `"Bottom"`, the popover knob will be placed at the bottom of the popover.
382
+ * }
383
+ * @return <String> The SVG path.
384
+ */
385
+ _pathForPopoverWithFrame(frame, {widthIndicatorSize: size = 8, radius = 8, inset = 0, knobPosition = undefined, gravity = 'Top'} = {widthIndicatorSize: 8, radius: 4, inset: 0}) {
386
+ let top = gravity === 'Bottom' ? 0 : size * Math.SQRT2 / 2 | 0;
387
+ let bottom = gravity === 'Bottom' ? frame.size.height - size * Math.SQRT2 / 2 | 0 : frame.size.height;
388
+ const left = inset;
389
+
390
+ if (inset) {
391
+ size = size - inset;
392
+ frame = frame.copy();
393
+ frame.insetWithInset(BMInsetMakeWithEqualInsets(inset));
394
+ radius = radius + inset;
395
+ top += inset;
396
+ bottom -= inset;
397
+ }
398
+
399
+ if (knobPosition === undefined) {
400
+ knobPosition = frame.size.width / 2 | 0;
401
+ }
402
+ else {
403
+ knobPosition = (knobPosition - inset) | 0;
404
+ }
405
+
406
+ const knobWidth = size * Math.SQRT2;
407
+ const knobHeight = knobWidth / 2 | 0;
408
+
409
+ // bottom should be equivalent to frame.size.height + inset
410
+ const pathTop = gravity === 'Bottom' ?
411
+ `M${left + radius},${top} L${frame.size.width + left - radius},${top} ` :
412
+ `M${left + radius},${top} L${(knobPosition - knobWidth / 2 + left)},${top} l${(knobWidth / 2)},${-knobHeight} l${(knobWidth / 2)},${knobHeight} L${frame.size.width + left - radius},${top} `;
413
+
414
+ const pathBottom = gravity === 'Bottom' ?
415
+ `Q${frame.size.width + left},${bottom} ${frame.size.width + left - radius},${bottom} L${(knobPosition + knobWidth / 2 + left)},${bottom} l${(-knobWidth / 2)},${knobHeight} l${(-knobWidth / 2)},${-knobHeight} L${radius},${bottom} ` :
416
+ `Q${frame.size.width + left},${bottom} ${frame.size.width + left - radius},${bottom} L${radius},${bottom} `;
417
+
418
+
419
+ let path = pathTop;
420
+ path += `Q${frame.size.width + left},${top} ${frame.size.width + left},${top + radius} L${frame.size.width + left},${bottom - radius} `;
421
+ path += pathBottom;
422
+ path += `Q${left},${bottom} ${left},${bottom - radius} L${left},${top + radius} Q${left},${top} ${radius},${top} Z`;
423
+
424
+ return path;
425
+ },
426
+
427
+ // @override - BMWindow
428
+ animateInWithCompletionHandler(completionHandler) {
429
+ const popoverLayers = [this.contentNode, this._background, this._dropShadowContainer];
430
+
431
+ this.node.style.opacity = 1;
432
+
433
+ let first = YES;
434
+ for (const layer of popoverLayers) {
435
+ BMHook(layer, {scaleX: .75, scaleY: .75, opacity: 0});
436
+
437
+ __BMVelocityAnimate(layer, {scaleX: 1, scaleY: 1, opacity: 1}, {duration: 300, easing: [0,1.59,.49,1], complete: first ? completionHandler : undefined}, YES);
438
+ first = NO;
439
+ }
440
+ },
441
+
442
+ // @override - BMWindow
443
+ animateOutWithCompletionHandler(completionHandler) {
444
+ const popoverLayers = [this.contentNode, this._background, this._dropShadowContainer];
445
+
446
+ const self = this;
447
+
448
+ let first = YES;
449
+ for (const layer of popoverLayers) {
450
+ layer.style.pointerEvents = 'none';
451
+
452
+ const runCompletionHandler = first;
453
+ __BMVelocityAnimate(layer, {scaleX: .9, scaleY: .9, opacity: 0}, {duration: 200, easing: 'easeInOutQuart', complete() {
454
+ if (runCompletionHandler) {
455
+ self.node.style.opacity = 0;
456
+ completionHandler();
457
+ }
458
+ }}, YES);
459
+ first = NO;
460
+ }
461
+ },
462
+
463
+ // @override - BMWindow
464
+ bringToFrontAnimated(animated, args) {
465
+ if (!this.anchorNode && !this.anchorPoint && !this.anchorRect) throw new Error('The anchorPoint, anchorRect or anchorNode must be set prior to showing this popover.');
466
+
467
+ this._updatePosition();
468
+
469
+ BMWindow.prototype.bringToFrontAnimated.apply(this, arguments);
470
+ },
471
+
472
+ // @override - BMWindow
473
+ release() {
474
+ if (this._clipPathUUID) {
475
+ this._clipPathSVG.remove();
476
+ }
477
+
478
+ return BMWindow.prototype.release.apply(this, arguments);
479
+ }
480
+
481
+ });
482
+
483
+ /**
484
+ * Constructs and returns a popover with the given size.
485
+ * @param size <BMSize> The popover's size.
486
+ * @return <BMPopover> A popover.
487
+ */
488
+ BMPopover.popoverWithSize = function (size) {
489
+ return (new BMPopover).initWithSize(size);
490
+ }
491
+
492
+ // @endtype