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,512 @@
1
+ import { BMExtend, NO } from "../Core/BMCoreUI";
2
+ import { BMView } from "./BMView_v2.5";
3
+ import { BMMenuKind } from "./BMMenu";
4
+
5
+ // @ts-check
6
+
7
+ const BM_TEXT_FIELD_MAX_SUGGESTIONS = 10;
8
+
9
+ // @type BMTextField extends BMView
10
+
11
+ /**
12
+ * A text field is a vew wrapper around an `input` type `"text"` element that adds additional
13
+ * functionalities such as a drop-down with suggestions and auto completion.
14
+ */
15
+ export function BMTextField() {} // <constructor>
16
+
17
+ BMTextField.prototype = BMExtend(Object.create(BMView.prototype), {
18
+ constructor: BMTextField,
19
+
20
+ // @override - BMView
21
+ supportsAutomaticIntrinsicSize: NO,
22
+
23
+ // @override - BMView
24
+ supportsIntrinsicSize: NO,
25
+
26
+ /**
27
+ * Used to control whether this text field autocompletes based on suggestions obtained from its delegate object.
28
+ * When this property is set to `YES`, whenever the text that the user is typing matches one of the suggestions
29
+ * the rest of the text will be automatically filled in and selected.
30
+ *
31
+ * When this property is set to `NO`, suggestions will still appear as a drop down menu, but the text will not be automatically completed.
32
+ */
33
+ _autoCompletes: YES, // <Boolean>
34
+ get autoCompletes() {
35
+ return this._autoCompletes;
36
+ },
37
+ set autoCompletes(completes) {
38
+ this._autoCompletes = completes;
39
+ },
40
+
41
+ /**
42
+ * Used to control whether this text field supports suggestions that appear for the user as they are typing in this text field.
43
+ * When this property is set to `YES`, if there are any suggestions obtained from the delegate object, the matching ones will appear
44
+ * as a drop down below the text field. The text field will automatically select the first matching suggestion and fill it in if the user
45
+ * presses the `return` key.
46
+ *
47
+ * When this property is set to `NO`, suggestions will be unavailable. This will also disable auto completion.
48
+ */
49
+ _usesSuggestions: YES, // <Boolean>
50
+ get usesSuggestions() {
51
+ return this._usesSuggestions;
52
+ },
53
+ set usesSuggestions(uses) {
54
+ if (uses != this._usesSuggestions) {
55
+ this._usesSuggestions = uses;
56
+
57
+ if (uses) {
58
+ this._setUpEventHandlers();
59
+ }
60
+ else {
61
+ this._clearEventHandlers();
62
+ }
63
+ }
64
+ },
65
+
66
+ /**
67
+ * An optional delegate that can be used to respond to various callbacks from the text field.
68
+ */
69
+ delegate: undefined, // <BMTextFieldDelegate, nullable>
70
+
71
+ // @override - BMView
72
+ initWithDOMNode(node) {
73
+ BMView.prototype.initWithDOMNode.apply(this, arguments);
74
+
75
+ if (this._usesSuggestions) this._setUpEventHandlers();
76
+
77
+ return this;
78
+ },
79
+
80
+ /**
81
+ * The focus event listener.
82
+ */
83
+ _focusHandler: undefined, // <Promise<void> ^(Event)>
84
+
85
+ /**
86
+ * The focus event listener.
87
+ */
88
+ _blurHandler: undefined, // <void ^(Event)>
89
+
90
+ /**
91
+ * The focus event listener.
92
+ */
93
+ _keydownHandler: undefined, // <void ^(Event)>
94
+
95
+ /**
96
+ * The focus event listener.
97
+ */
98
+ _inputHandler: undefined, // <void ^(Event)>
99
+
100
+ /**
101
+ * The paste event listener.
102
+ */
103
+ _pasteHandler: undefined, // <void ^(Event)>
104
+
105
+ /**
106
+ * Sets up the event handlers for this text field to handle suggestions.
107
+ */
108
+ _setUpEventHandlers() {
109
+ /** @type {HTMLInputElement} */ const inputNode = this.node;
110
+
111
+ // This array contains all the available layout variables and is initialized upon the input
112
+ // element acquiring keyboard focus
113
+ let suggestions = [];
114
+ // This array contains the layout variables that match the text contained within the input element
115
+ let filteredSuggestions = [];
116
+ // The popup menu displaying the filtered suggestions
117
+ let menu;
118
+ // The index of the top hit suggestion. An index of -1 represents no top hit.
119
+ let highlightedItemIndex;
120
+
121
+ // TODO: Consider encapsulating this functionality into a separate auto-complete text box class
122
+ inputNode.addEventListener('focus', this._focusHandler = async event => {
123
+ // If suggestions are not enabled, no additional action is required
124
+ if (!this._usesSuggestions) return;
125
+
126
+ // TODO: Consider whether this is appropriate for this class
127
+ // Select all content upon receiving focus
128
+ // inputNode.select();
129
+
130
+ let options = [];
131
+
132
+ if (this.delegate && this.delegate.textFieldSuggestionsForText) {
133
+ suggestions = this.delegate.textFieldSuggestionsForText(this, this.value);
134
+ }
135
+ else return;
136
+
137
+ filteredSuggestions = suggestions;
138
+
139
+ highlightedItemIndex = -1;
140
+
141
+ let limit = BM_TEXT_FIELD_MAX_SUGGESTIONS;
142
+ for (const option of suggestions) {
143
+ options.push(this._menuItemWithLabel(option, {action: () => {
144
+ menu = undefined;
145
+ inputNode.value = option;
146
+
147
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
148
+ this.delegate.textFieldContentsDidChange(this);
149
+ }
150
+
151
+ // Remove focus from the text box upon selecting a value
152
+ inputNode.blur();
153
+ }}));
154
+
155
+ // Limit to 10 items
156
+ limit--;
157
+ if (!limit) break;
158
+ }
159
+
160
+ // Create and show a pulldown menu below the constant textbox, if there are any suggestions
161
+ let menuShouldDisplay = YES;
162
+ if (this.delegate.textFieldShouldShowSuggestions) {
163
+ menuShouldDisplay = this.delegate.textFieldShouldShowSuggestions(this);
164
+ }
165
+
166
+ if (menuShouldDisplay && suggestions.length) {
167
+ let frame = BMRectMakeWithNodeFrame(inputNode);
168
+ let point = BMPointMake(frame.origin.x, frame.bottom);
169
+ menu = this._showMenuAtPoint(point, {withOptions: options, kind: BMMenuKind.PullDownMenu});
170
+ menu.classList.add('BMLayoutEditorConstantSuggestions');
171
+
172
+ menu.parentNode.classList.add('BMLayoutEditorConstantSuggestionsOverlay');
173
+
174
+ // Prevent the menu from stealing focus, which would dismiss it before an option is selected
175
+ menu.addEventListener('mousedown', e => e.preventDefault(), YES);
176
+ }
177
+
178
+ const fn = event => event.preventDefault();
179
+ inputNode.addEventListener('mouseup', fn);
180
+
181
+ // This timeout is necessary because otherwise regular clicks would instantly dismiss the menu
182
+ await new Promise(resolve => setTimeout(resolve, 200));
183
+ inputNode.removeEventListener('mouseup', fn);
184
+
185
+ });
186
+
187
+ // The backspace and delete keys are not identifiable in input events
188
+ // So they are detected on keydown prior to the input event occurring
189
+ let isBackspaceInput = NO;
190
+
191
+ inputNode.addEventListener('keydown', this._keydownHandler = event => {
192
+ // If suggestions are not enabled, no additional action is required
193
+ if (!this._usesSuggestions) return;
194
+
195
+ if (event.code == 'ArrowDown' || event.code == 'ArrowUp') {
196
+ event.preventDefault();
197
+ if (filteredSuggestions.length == 0) return;
198
+
199
+ // Move the selection accordingly
200
+ if (event.code == 'ArrowUp') {
201
+ highlightedItemIndex = Math.max((highlightedItemIndex || 0) - 1, 0);
202
+ }
203
+ else {
204
+ highlightedItemIndex = Math.min((isNaN(highlightedItemIndex) ? -1 : highlightedItemIndex) + 1, filteredSuggestions.length - 1);
205
+ }
206
+
207
+ // Move the menu highlight
208
+ if (menu) {
209
+ // Remove the highlighted class from the previously selected item
210
+ for (const element of menu.querySelectorAll('.BMLayoutEditorConstraintPopupOptionHighlighted')) {
211
+ element.classList.remove('BMLayoutEditorConstraintPopupOptionHighlighted');
212
+ }
213
+
214
+ // Highlight the current item
215
+ menu.childNodes[highlightedItemIndex].classList.add('BMLayoutEditorConstraintPopupOptionHighlighted');
216
+ }
217
+
218
+ }
219
+
220
+ if (event.code == 'Enter') {
221
+ // Allow the delegate to supress the default action
222
+ let shouldReturn = YES;
223
+ if (this.delegate && this.delegate.textFieldShouldReturn) {
224
+ shouldReturn = this.delegate.textFieldShouldReturn(this);
225
+ }
226
+
227
+ if (typeof shouldReturn == 'undefined') shouldReturn = YES;
228
+
229
+ if (!shouldReturn) return;
230
+
231
+ // Upon pressing the enter key, select the currently highlighted item, if available
232
+ // then remove focus from the constant box
233
+ let performAutocomplete = NO;
234
+ if (!isNaN(highlightedItemIndex) && filteredSuggestions.length && filteredSuggestions[highlightedItemIndex]) {
235
+ if (this.delegate && this.delegate.textFieldShouldAutocompleteText) {
236
+ performAutocomplete = this.delegate.textFieldShouldAutocompleteText(this, inputNode.value, {withSuggestion: filteredSuggestions[highlightedItemIndex]});
237
+ }
238
+
239
+ if (performAutocomplete) {
240
+ inputNode.value = filteredSuggestions[highlightedItemIndex];
241
+
242
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
243
+ this.delegate.textFieldContentsDidChange(this);
244
+ }
245
+ }
246
+ }
247
+
248
+ inputNode.blur();
249
+ }
250
+
251
+ if (event.code == 'Backspace' || event.code == 'Delete') {
252
+ isBackspaceInput = YES;
253
+ }
254
+
255
+ });
256
+
257
+ inputNode.addEventListener('input', this._inputHandler = event => {
258
+ // If suggestions are not enabled, no additional action is required
259
+ if (!this._usesSuggestions) {
260
+ if (!textFieldDidHighlightSuggestion) {
261
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
262
+ this.delegate.textFieldContentsDidChange(this);
263
+ }
264
+ }
265
+ }
266
+
267
+ // Request new suggestions from the delegate for the new text
268
+ if (this.delegate && this.delegate.textFieldSuggestionsForText) {
269
+ suggestions = this.delegate.textFieldSuggestionsForText(this, this.value);
270
+ }
271
+
272
+ // If the menu is visible, filter it accordingly
273
+ filteredSuggestions = suggestions.filter(e => e.toLowerCase().startsWith(inputNode.value.toLowerCase()));
274
+
275
+ // Limit to the topmost 10 items
276
+ filteredSuggestions.length = Math.min(filteredSuggestions.length, 10);
277
+
278
+ // Empty the menu
279
+ if (menu) menu.innerHTML = '';
280
+
281
+ // Reset the highlighted item index
282
+ highlightedItemIndex = 0;
283
+
284
+ // Then add the filtered options
285
+ let limit = BM_TEXT_FIELD_MAX_SUGGESTIONS;
286
+ if (menu) for (const option of filteredSuggestions) {
287
+ menu.appendChild(this._menuItemWithLabel(option, {action: () => {
288
+ menu = undefined;
289
+ inputNode.value = option;
290
+
291
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
292
+ this.delegate.textFieldContentsDidChange(this);
293
+ }
294
+ }}));
295
+
296
+ // Limit to 10 items
297
+ limit--;
298
+ if (!limit) break;
299
+ }
300
+
301
+ // Set to YES if the change event is fired due to autocompleting the text in the text field
302
+ let textFieldDidHighlightSuggestion = NO;
303
+
304
+ // Highlight the first item
305
+ if (filteredSuggestions.length) {
306
+ if (menu) {
307
+ menu.childNodes[0].classList.add('BMLayoutEditorConstraintPopupOptionHighlighted');
308
+ menu.style.display = 'block';
309
+
310
+ // Need to reposition the menu if it appears above the text box as it may float at a distance
311
+ // due to items being removed
312
+ let frame = BMRectMakeWithNodeFrame(inputNode);
313
+ let point = BMPointMake(frame.origin.x, frame.bottom);
314
+ this._repositionMenuAtPoint(point);
315
+ }
316
+
317
+ // Autocomplete the suggestion, if the caret is at the end of the text field
318
+ // and if this input isn't the result of pressing backspace or delete
319
+ if (this._autoCompletes && !isBackspaceInput) {
320
+ const selectionStart = inputNode.selectionStart;
321
+ if (selectionStart == inputNode.value.length) {
322
+ textFieldDidHighlightSuggestion = YES;
323
+
324
+ inputNode.value = filteredSuggestions[0];
325
+ // Select the remainder of the suggestion
326
+ inputNode.setSelectionRange(selectionStart, inputNode.value.length, "backward");
327
+
328
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
329
+ this.delegate.textFieldContentsDidChange(this);
330
+ }
331
+ }
332
+ }
333
+
334
+ // Clear the backspace flag upon processing it once
335
+ if (isBackspaceInput) {
336
+ isBackspaceInput = NO;
337
+ }
338
+ }
339
+ else {
340
+ if (menu) menu.style.display = 'none';
341
+ }
342
+
343
+ if (!textFieldDidHighlightSuggestion) {
344
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
345
+ this.delegate.textFieldContentsDidChange(this);
346
+ }
347
+ }
348
+ });
349
+
350
+ // Upon the value box losing focus, dismiss the menu, if it was open
351
+ // unless the newly focused item is a descendant of the menu
352
+ inputNode.addEventListener('blur', this._blurHandler = event => {
353
+ if (menu) {
354
+ menu.parentNode.dispatchEvent(new Event('click'));
355
+ }
356
+ });
357
+
358
+ inputNode.addEventListener('paste', this._pasteHandler = event => {
359
+ if (this.delegate && this.delegate.textFieldContentsDidChange) {
360
+ this.delegate.textFieldContentsDidChange(this);
361
+ }
362
+ });
363
+ },
364
+
365
+ /**
366
+ * Clears the event handlers for this text field when suggestions are disabled.
367
+ */
368
+ _clearEventHandlers() {
369
+ this.node.removeEventListener('focus', this._focusHandler);
370
+ this.node.removeEventListener('blur', this._blurHandler);
371
+ this.node.removeEventListener('keydown', this._keydownHandler);
372
+ this.node.removeEventListener('input', this._inputHandler);
373
+ this.node.removeEventListener('input', this._pasteHandler);
374
+ },
375
+
376
+ /**
377
+ * Constructs and returns a DOM node for a menu item with the given label.
378
+ * @param label <String> The text to display in the menu item.
379
+ * {
380
+ * @param action <void ^(Event)> An action that occurs when the menu item is selected.
381
+ * }
382
+ * @return <DOMNode> A DOM node to be added to a menu.
383
+ */
384
+ _menuItemWithLabel(label, args) {
385
+ // TODO: Roll up into BMMenu
386
+ let option = document.createElement('div');
387
+ option.className = 'BMLayoutEditorConstraintPopupOption';
388
+ option.innerText = label;
389
+
390
+ option.addEventListener('click', args.action);
391
+
392
+ return option;
393
+ },
394
+
395
+
396
+
397
+ /**
398
+ * Creates and brings up a context menu at the given coordinates, relative to the viewport.
399
+ * @param point <BMPoint> The point at which to show the menu.
400
+ * {
401
+ * @param withOptions <[DOMNode]> The options that the menu should display.
402
+ * @param kind <BMMenuKind, nullable> Defaults to .Menu. The kind of menu to show.
403
+ * }
404
+ * @return <DOMNode> The menu element.
405
+ */
406
+ _showMenuAtPoint(point, args) {
407
+ // TODO: Roll up into BMMenu
408
+ let constraintPopup = document.createElement('div');
409
+ constraintPopup.className = 'BMLayoutEditorConstraintPopup BMTextFieldSuggestions';
410
+
411
+ menu = constraintPopup;
412
+
413
+ let constraintPopupContainer = document.createElement('div');
414
+ constraintPopupContainer.className = 'BMLayoutEditorConstraintPopupContainer';
415
+ if (!('backdropFilter' in document.body.style) && !('webkitBackdropFilter' in document.body.style)) {
416
+ constraintPopup.style.backgroundColor = 'white';
417
+ }
418
+ constraintPopupContainer.appendChild(constraintPopup);
419
+
420
+ args.withOptions.forEach(item => constraintPopup.appendChild(item));
421
+
422
+ BMHook(constraintPopup, {scaleX: args.kind == BMMenuKind.PullDownMenu ? 1 : .75, scaleY: .75, opacity: 0});
423
+
424
+ document.body.appendChild(constraintPopupContainer);
425
+
426
+ this._repositionMenuAtPoint(point);
427
+
428
+ constraintPopup.addEventListener('click', event => event.preventDefault());
429
+
430
+ (window.Velocity || $.Velocity).animate(constraintPopup, {scaleX: 1, scaleY: 1, opacity: 1, translateZ: 0}, {
431
+ duration: 200,
432
+ easing: 'easeOutQuad',
433
+ complete: _ => ((constraintPopup.style.pointerEvents = 'all'), constraintPopupContainer.style.pointerEvents = 'all')
434
+ });
435
+
436
+ let delay = 0;
437
+ for (let child of constraintPopup.childNodes) {
438
+ BMHook(child, {translateY: '16px', translateZ: 0, opacity: 0});
439
+ (window.Velocity || $.Velocity).animate(child, {translateY: '0px', translateZ: 0, opacity: 1}, {
440
+ duration: 100,
441
+ easing: 'easeOutQuad',
442
+ delay: delay
443
+ });
444
+ delay += 16;
445
+ }
446
+
447
+ constraintPopupContainer.addEventListener('click', event => {
448
+ constraintPopup.style.pointerEvents = 'none';
449
+ constraintPopupContainer.style.pointerEvents = 'none';
450
+ let delay = constraintPopup.childNodes.length * 16 + 100 - 200;
451
+ delay = (delay < 0 ? 0 : delay);
452
+
453
+ (window.Velocity || $.Velocity).animate(constraintPopup, {scaleX: args.kind == BMMenuKind.PullDownMenu ? 1 : .75, scaleY: .75, opacity: 0, translateZ: 0}, {
454
+ duration: 200,
455
+ easing: 'easeInQuad',
456
+ delay: delay,
457
+ complete: _ => constraintPopupContainer.remove()
458
+ });
459
+
460
+ delay = 0;
461
+ for (let i = constraintPopup.childNodes.length - 1; i >= 0; i--) {
462
+ let child = constraintPopup.childNodes[i];
463
+ (window.Velocity || $.Velocity).animate(child, {translateY: '16px', translateZ: 0, opacity: 0}, {
464
+ duration: 100,
465
+ easing: 'easeInQuad',
466
+ delay: delay
467
+ });
468
+ delay += 16;
469
+ }
470
+ });
471
+
472
+ return constraintPopup;
473
+ },
474
+
475
+ _repositionMenuAtPoint(point) {
476
+ let height = menu.offsetHeight;
477
+
478
+ menu.style.transformOrigin = '50% 0%';
479
+ if (height + point.y > document.documentElement.clientHeight) {
480
+ menu.style.transformOrigin = '50% 100%';
481
+ point.y -= height + this.frame.size.height;
482
+ }
483
+
484
+ menu.style.left = point.x + 'px';
485
+ menu.style.top = point.y + 'px';
486
+ }
487
+
488
+ });
489
+
490
+ /**
491
+ * Constructs and returns a text field with a new DOM node.
492
+ * @return <BMTextField> A text field.
493
+ */
494
+ BMTextField.textField = function () {
495
+ const node = document.createElement('input');
496
+ node.type = 'text';
497
+
498
+ const textField = Object.create(BMTextField.prototype).initWithDOMNode(node);
499
+
500
+ return textField;
501
+ }
502
+
503
+ /**
504
+ * Constructs and returns a text field for the given input DOM node.
505
+ * @param node <DOMNode> An input DOM node.
506
+ * @return <BMTextField> A text field.
507
+ */
508
+ BMTextField.textFieldForInputNode = function (node) {
509
+ return Object.create(BMTextField.prototype).initWithDOMNode(node);
510
+ }
511
+
512
+ // @endtype
@@ -0,0 +1,71 @@
1
+ // @type interface BMTextFieldDelegate
2
+
3
+ /**
4
+ * The specification for a `BMTextField` object, which can optionally be used in conjunction with `BMTextField` objects and will receive various
5
+ * callbacks related to the text field's lifecycle.
6
+ */
7
+ function BMTextFieldDelegate() {} // <constructor>
8
+
9
+ BMTextFieldDelegate.prototype = {
10
+
11
+ /**
12
+ * Invoked by text fields to retrieve the available suggestions for autocomplete for the given text.
13
+ * This method should return the full list of suggestions available for the given text. The text field will further filter out the suggestions
14
+ * based on the text that the user has typed.
15
+ *
16
+ * If this method is not implemented, suggestions and autocomplete will not be available.
17
+ * @param textField <BMTextField> The calling text field.
18
+ * @param text <String>
19
+ * @return <[String]> An array of string suggestions.
20
+ */
21
+ textFieldSuggestionsForText(textField, text) {},
22
+
23
+ /**
24
+ * Invoked whenever the text field is about to show the suggestions drop down, if suggestions are available.
25
+ * Delegate objects can implement this method to control whether the drop down should be displayed.
26
+ *
27
+ * If this method is not implemented, the suggestions drop down will be displayed whenever appropriate.
28
+ * @param textField <BMTextField> The calling text field.
29
+ * @return <Boolean> `YES` if the suggestions drop down should appear, `NO` otherwise.
30
+ */
31
+ textFieldShouldShowSuggestions(textField) {},
32
+
33
+ /**
34
+ * Invoked whenever the text field is about to autocomplete the text with the given suggestion. This occurs when the
35
+ * user presses the return key.
36
+ * Delegate objects can implement this method to control whether the autocomplete should be performed.
37
+ *
38
+ * If autocomplete is enabled and this method is not implemented, the autocomplete will not be performed.
39
+ * @param textField <BMTextField> The calling text field.
40
+ * @param text <String> The text that has been typed in this text field.
41
+ * {
42
+ * @param withSuggestion <String> The suggestion
43
+ * }
44
+ */
45
+ textFieldShouldAutocompleteText(textField, text, {withSuggestion}) {},
46
+
47
+ /**
48
+ * Invoked whenever the contents in this text field change for any reason. This can be because of an input event,
49
+ * the user pasting text or a suggestion being selected.
50
+ * Note that this method will not be invoked when manually setting the input node's `value`.
51
+ *
52
+ * Delegate objects can implement this method in place of standard event listeners to handle changes to this text field.
53
+ * The contents of the text field can be retrieved via the standard `value` property on the text field's node.
54
+ * @param textField <BMTextField> The calling text field.
55
+ */
56
+ textFieldContentsDidChange(textField) {},
57
+
58
+ /**
59
+ * Invoked whenever the user presses the return key while the text field has keyboard focus.
60
+ *
61
+ * Delegate objects can implement this method to customize the behaviour when the user presses the return key and also control whether the default action will take place.
62
+ * The default action for pressing the return key is to cause the text field to resign keyboard focus. If suggestions are used, the return key will also fill in the currently
63
+ * highlighted suggestion.
64
+ * @param textField <BMTextField> The calling text field.
65
+ * @return <Boolean, nullable> Defaults to `YES`. If set to `YES`, the standard behaviour will be invoked.
66
+ */
67
+ textFieldShouldReturn() {}
68
+
69
+ };
70
+
71
+ // @endtype