@nativescript-community/ui-bottomsheet 0.0.0-stage → 1.0.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.
package/index.ios.js ADDED
@@ -0,0 +1,678 @@
1
+ import { Application, Page, Trace, Utils, View, fromObject } from '@nativescript/core';
2
+ import { applyMixins } from '@nativescript-community/ui-core';
3
+ import { StateBottomSheet, ViewWithBottomSheetBase } from './index-common';
4
+ import { MDLayoutViewController, getCatalystDetentHeights, getPresentingController, setCatalystVisibleHeight } from './ios-layout';
5
+ export { ViewWithBottomSheetBase } from './index-common';
6
+ const majorVersion = Utils.ios.MajorVersion;
7
+ const CONTENT_DETENT = 'content';
8
+ const PEEK_DETENT = 'peek';
9
+ const LARGE_DETENT = 'large';
10
+ // UIKit shows a sheet as a window modal one on Mac Catalyst: the sheet is then a card at the bottom of the window
11
+ // isMacCatalystApp is not always in the runtime metadata: fall back to the KVC key
12
+ function detectCatalyst() {
13
+ const info = NSProcessInfo.processInfo;
14
+ if (typeof info.isMacCatalystApp === 'boolean') {
15
+ return info.isMacCatalystApp;
16
+ }
17
+ try {
18
+ return !!info.valueForKey('macCatalystApp');
19
+ }
20
+ catch (error) {
21
+ return false;
22
+ }
23
+ }
24
+ const isCatalyst = (typeof __CATALYST__ !== 'undefined' && !!__CATALYST__) || detectCatalyst();
25
+ const CATALYST_GRABBER_AREA = { width: 120, height: 24 };
26
+ const CATALYST_SCRIM_ALPHA = 0.32;
27
+ var SheetDelegateImpl = /** @class */ (function (_super) {
28
+ __extends(SheetDelegateImpl, _super);
29
+ function SheetDelegateImpl() {
30
+ return _super !== null && _super.apply(this, arguments) || this;
31
+ }
32
+ SheetDelegateImpl.initWithOwner = function (owner) {
33
+ var delegate = SheetDelegateImpl.new();
34
+ delegate._owner = new WeakRef(owner);
35
+ return delegate;
36
+ };
37
+ SheetDelegateImpl.prototype.sheetPresentationControllerDidChangeSelectedDetentIdentifier = function (sheet) {
38
+ var _a;
39
+ var owner = this._owner.get();
40
+ if (owner === null || owner === void 0 ? void 0 : owner._onChangeStateBottomSheetCallback) {
41
+ owner._onChangeStateBottomSheetCallback(sheet.selectedDetentIdentifier === LARGE_DETENT || sheet.selectedDetentIdentifier === CONTENT_DETENT ? StateBottomSheet.EXPANDED : StateBottomSheet.COLLAPSED);
42
+ }
43
+ (_a = owner === null || owner === void 0 ? void 0 : owner._onDetentChange) === null || _a === void 0 ? void 0 : _a.call(owner, owner.getBottomSheetDetentIndex());
44
+ };
45
+ SheetDelegateImpl.prototype.presentationControllerShouldDismiss = function (presentationController) {
46
+ var owner = this._owner.get();
47
+ return (owner === null || owner === void 0 ? void 0 : owner._onWillDismiss) ? owner._onWillDismiss() !== false : true;
48
+ };
49
+ SheetDelegateImpl.prototype.presentationControllerDidAttemptToDismiss = function (presentationController) {
50
+ var _a, _b;
51
+ (_b = (_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._onDismissAttempt) === null || _b === void 0 ? void 0 : _b.call(_a);
52
+ };
53
+ SheetDelegateImpl.prototype.presentationControllerDidDismiss = function (presentationController) {
54
+ var _a;
55
+ // called when dismissed by the user (dragging down or background tap)
56
+ var owner = this._owner.get();
57
+ if (owner) {
58
+ (_a = owner._onChangeStateBottomSheetCallback) === null || _a === void 0 ? void 0 : _a.call(owner, StateBottomSheet.CLOSED);
59
+ owner._unloadBottomSheet();
60
+ }
61
+ };
62
+ SheetDelegateImpl.ObjCProtocols = [UISheetPresentationControllerDelegate];
63
+ return SheetDelegateImpl;
64
+ }(NSObject));
65
+ var OutsideTapTarget = /** @class */ (function (_super) {
66
+ __extends(OutsideTapTarget, _super);
67
+ function OutsideTapTarget() {
68
+ return _super !== null && _super.apply(this, arguments) || this;
69
+ }
70
+ OutsideTapTarget.initWithOwner = function (owner) {
71
+ var target = OutsideTapTarget.new();
72
+ target._owner = new WeakRef(owner);
73
+ return target;
74
+ };
75
+ OutsideTapTarget.prototype.outsideTapped = function () {
76
+ var _a;
77
+ (_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._onOutsideTap();
78
+ };
79
+ OutsideTapTarget.ObjCExposedMethods = {
80
+ outsideTapped: { returns: interop.types.void, params: [] }
81
+ };
82
+ return OutsideTapTarget;
83
+ }(NSObject));
84
+ var CatalystCardTarget = /** @class */ (function (_super) {
85
+ __extends(CatalystCardTarget, _super);
86
+ function CatalystCardTarget() {
87
+ return _super !== null && _super.apply(this, arguments) || this;
88
+ }
89
+ CatalystCardTarget.initWithOwner = function (owner) {
90
+ var target = CatalystCardTarget.new();
91
+ target._owner = new WeakRef(owner);
92
+ return target;
93
+ };
94
+ CatalystCardTarget.prototype.cardPanned = function (recognizer) {
95
+ var _a;
96
+ (_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._onCatalystPan(recognizer);
97
+ };
98
+ CatalystCardTarget.prototype.cardScrolled = function (recognizer) {
99
+ var _a;
100
+ (_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._onCatalystScroll(recognizer);
101
+ };
102
+ CatalystCardTarget.prototype.grabberTapped = function () {
103
+ var _a;
104
+ (_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._cycleCatalystDetent();
105
+ };
106
+ CatalystCardTarget.prototype.gestureRecognizerShouldBegin = function (recognizer) {
107
+ var _a, _b;
108
+ // the tap outside has this delegate too
109
+ if (!(recognizer instanceof UIPanGestureRecognizer)) {
110
+ return true;
111
+ }
112
+ return (_b = (_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._shouldBeginCatalystPan(recognizer)) !== null && _b !== void 0 ? _b : false;
113
+ };
114
+ CatalystCardTarget.prototype.gestureRecognizerShouldReceiveTouch = function (recognizer, touch) {
115
+ var _a;
116
+ // the tap outside: not for the touches in the card
117
+ return !(recognizer instanceof UITapGestureRecognizer) || !((_a = this._owner.get()) === null || _a === void 0 ? void 0 : _a._isInCatalystCard(touch.view));
118
+ };
119
+ CatalystCardTarget.prototype.gestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer = function (recognizer, other) {
120
+ // the scrolling of the content runs along, it is pinned while the card moves
121
+ return other.view instanceof UIScrollView;
122
+ };
123
+ CatalystCardTarget.ObjCProtocols = [UIGestureRecognizerDelegate];
124
+ CatalystCardTarget.ObjCExposedMethods = {
125
+ cardPanned: { returns: interop.types.void, params: [interop.types.id] },
126
+ cardScrolled: { returns: interop.types.void, params: [interop.types.id] },
127
+ grabberTapped: { returns: interop.types.void, params: [] }
128
+ };
129
+ return CatalystCardTarget;
130
+ }(NSObject));
131
+ var CatalystPassThroughView = /** @class */ (function (_super) {
132
+ __extends(CatalystPassThroughView, _super);
133
+ function CatalystPassThroughView() {
134
+ return _super !== null && _super.apply(this, arguments) || this;
135
+ }
136
+ CatalystPassThroughView.prototype.hitTestWithEvent = function (point, event) {
137
+ var hit = _super.prototype.hitTestWithEvent.call(this, point, event);
138
+ return hit === this ? null : hit;
139
+ };
140
+ return CatalystPassThroughView;
141
+ }(UIView));
142
+ export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
143
+ constructor() {
144
+ super(...arguments);
145
+ this._detentIds = [];
146
+ }
147
+ _onOutsideTap() {
148
+ const sheet = this.bottomSheetController?.sheetPresentationController;
149
+ const undimmedId = sheet?.largestUndimmedDetentIdentifier;
150
+ // the system dimming view handles the taps at the larger detents
151
+ if (undimmedId && sheet.selectedDetentIdentifier !== undimmedId && this._detentIds.indexOf(sheet.selectedDetentIdentifier) > this._detentIds.indexOf(undimmedId)) {
152
+ return;
153
+ }
154
+ if (this._onWillDismiss?.() === false) {
155
+ this._onDismissAttempt?.();
156
+ return;
157
+ }
158
+ this.closeBottomSheet();
159
+ }
160
+ _showNativeBottomSheet(parent, options) {
161
+ options.context = options.context || {};
162
+ let rootView = Application.getRootView();
163
+ if (rootView.parent) {
164
+ rootView = rootView.parent;
165
+ }
166
+ let currentView = parent.page || rootView;
167
+ currentView = currentView.modal || currentView;
168
+ const parentController = getPresentingController(parent);
169
+ if (!parentController.view || !parentController.view.window) {
170
+ Trace.write('Parent page is not part of the window hierarchy.', Trace.categories.ViewHierarchy, Trace.messageType.error);
171
+ return;
172
+ }
173
+ this.parent = Application.getRootView();
174
+ // dirty trick for RADSideDrawer. as we set parent for css variables/classes, _setupAsRootView
175
+ // will call _addViewToNativeVisualTree which breaks the bottomSheet with RADSiderDrawer
176
+ // so we disable _addViewToNativeVisualTree for _setupAsRootView
177
+ const oldAddViewToNativeVisualTree = this.parent._addViewToNativeVisualTree;
178
+ this.parent._addViewToNativeVisualTree = () => false;
179
+ this._setupAsRootView({});
180
+ this.parent._addViewToNativeVisualTree = oldAddViewToNativeVisualTree;
181
+ this._commonShowNativeBottomSheet(currentView, options);
182
+ let controller = this.viewController;
183
+ if (!controller) {
184
+ const nativeView = this.ios || this.nativeViewProtected;
185
+ controller = MDLayoutViewController.initWithOwner(this);
186
+ controller.ignoreBottomSafeArea = options.ignoreBottomSafeArea !== undefined ? options.ignoreBottomSafeArea : false;
187
+ controller.ignoreTopSafeArea = options.ignoreTopSafeArea !== undefined ? options.ignoreTopSafeArea : true;
188
+ if (isCatalyst && (options.canTouchBehind === true || options.disableDimBackground === true)) {
189
+ controller.view = CatalystPassThroughView.alloc().initWithFrame(controller.view.frame);
190
+ }
191
+ if (nativeView instanceof UIView) {
192
+ controller.view.addSubview(nativeView);
193
+ }
194
+ this.viewController = controller; // store the viewController so that safeArea overflow is applied correctly
195
+ }
196
+ this._raiseShowingBottomSheetEvent();
197
+ this.bottomSheetController = controller;
198
+ const catalystCard = isCatalyst;
199
+ controller.modalPresentationStyle = catalystCard ? 5 /* UIModalPresentationStyle.OverFullScreen */ : 1 /* UIModalPresentationStyle.PageSheet */;
200
+ if (catalystCard) {
201
+ controller.modalTransitionStyle = 2 /* UIModalTransitionStyle.CrossDissolve */;
202
+ this.configureCatalystCard(controller, options, parentController);
203
+ }
204
+ else {
205
+ const sheet = controller.sheetPresentationController;
206
+ if (sheet) {
207
+ this.configureSheet(sheet, controller, options, parentController);
208
+ }
209
+ }
210
+ controller.nsAnimated = true;
211
+ parentController.presentViewControllerAnimatedCompletion(controller, true, null);
212
+ if (catalystCard) {
213
+ this.styleCatalystCard(controller, options);
214
+ }
215
+ else if (options.transparent === true) {
216
+ controller.view.backgroundColor = UIColor.clearColor;
217
+ }
218
+ else {
219
+ if (!(this instanceof Page)) {
220
+ controller.view.backgroundColor = UIColor.systemBackgroundColor;
221
+ }
222
+ if (options.backgroundOpacity && controller.view.backgroundColor) {
223
+ controller.view.backgroundColor = controller.view.backgroundColor.colorWithAlphaComponent(options.backgroundOpacity);
224
+ }
225
+ }
226
+ const transitionCoordinator = controller.transitionCoordinator;
227
+ if (transitionCoordinator) {
228
+ UIViewControllerTransitionCoordinator.prototype.animateAlongsideTransitionCompletion.call(transitionCoordinator, null, () => {
229
+ this.bindingContext = fromObject(options.context);
230
+ this._raiseShownBottomSheetEvent();
231
+ });
232
+ }
233
+ else {
234
+ this.bindingContext = fromObject(options.context);
235
+ this._raiseShownBottomSheetEvent();
236
+ }
237
+ }
238
+ configureCatalystCard(controller, options, parentController) {
239
+ this._onDetentChange = options.onDetentChange;
240
+ this._onWillDismiss = options.onWillDismiss;
241
+ this._onDismissAttempt = options.onDismissAttempt;
242
+ this._catalystOptions = options;
243
+ let detents = options.detents;
244
+ if (!detents?.length) {
245
+ detents = options.peekHeight && options.skipCollapsedState !== true ? [options.peekHeight, CONTENT_DETENT] : [CONTENT_DETENT];
246
+ }
247
+ const card = { detents, index: Math.min(options.initialDetentIndex || 0, detents.length - 1) };
248
+ controller.catalystCard = card;
249
+ this._catalystTarget = CatalystCardTarget.initWithOwner(this);
250
+ const nativeView = this.nativeViewProtected;
251
+ if (nativeView instanceof UIView) {
252
+ const pan = UIPanGestureRecognizer.alloc().initWithTargetAction(this._catalystTarget, 'cardPanned');
253
+ pan.delegate = this._catalystTarget;
254
+ nativeView.addGestureRecognizer(pan);
255
+ // the wheel and the trackpad: the card opens first, then the content scrolls. They never close it
256
+ const scroll = UIPanGestureRecognizer.alloc().initWithTargetAction(this._catalystTarget, 'cardScrolled');
257
+ scroll.delegate = this._catalystTarget;
258
+ scroll.allowedTouchTypes = NSArray.arrayWithArray([]);
259
+ scroll.allowedScrollTypesMask = 3 /* UIScrollTypeMask.All */;
260
+ nativeView.addGestureRecognizer(scroll);
261
+ }
262
+ if (options.transparent !== true && options.grabber !== false) {
263
+ const area = UIView.alloc().initWithFrame(CGRectMake(0, 0, CATALYST_GRABBER_AREA.width, CATALYST_GRABBER_AREA.height));
264
+ const grabber = UIView.alloc().initWithFrame(CGRectMake((CATALYST_GRABBER_AREA.width - 36) / 2, 6, 36, 5));
265
+ grabber.backgroundColor = UIColor.tertiaryLabelColor;
266
+ grabber.layer.cornerRadius = 2.5;
267
+ area.addSubview(grabber);
268
+ area.addGestureRecognizer(UITapGestureRecognizer.alloc().initWithTargetAction(this._catalystTarget, 'grabberTapped'));
269
+ controller.view.addSubview(area);
270
+ card.grabberArea = area;
271
+ }
272
+ if (options.canTouchBehind === true || options.disableDimBackground === true) {
273
+ if (options.canTouchBehind !== true && options.dismissOnBackgroundTap !== false) {
274
+ // no scrim to tap: a recognizer on the content behind closes the sheet and keeps the touch from it
275
+ const view = parentController?.view;
276
+ if (view) {
277
+ this._outsideTapTarget = OutsideTapTarget.initWithOwner(this);
278
+ this._outsideTapRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(this._outsideTapTarget, 'outsideTapped');
279
+ this._outsideTapRecognizer.cancelsTouchesInView = true;
280
+ view.addGestureRecognizer(this._outsideTapRecognizer);
281
+ this._outsideTapView = view;
282
+ }
283
+ }
284
+ }
285
+ else if (options.dismissOnBackgroundTap !== false) {
286
+ this._outsideTapTarget = OutsideTapTarget.initWithOwner(this);
287
+ this._outsideTapRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(this._outsideTapTarget, 'outsideTapped');
288
+ this._outsideTapRecognizer.delegate = this._catalystTarget;
289
+ controller.view.addGestureRecognizer(this._outsideTapRecognizer);
290
+ this._outsideTapView = controller.view;
291
+ }
292
+ }
293
+ styleCatalystCard(controller, options) {
294
+ const dimmed = options.transparent !== true && options.canTouchBehind !== true && options.disableDimBackground !== true;
295
+ controller.view.backgroundColor = dimmed ? UIColor.blackColor.colorWithAlphaComponent(CATALYST_SCRIM_ALPHA) : UIColor.clearColor;
296
+ const nativeView = this.nativeViewProtected;
297
+ if (options.transparent === true || !(nativeView instanceof UIView)) {
298
+ return;
299
+ }
300
+ nativeView.backgroundColor = UIColor.systemBackgroundColor;
301
+ if (options.backgroundOpacity) {
302
+ nativeView.backgroundColor = nativeView.backgroundColor.colorWithAlphaComponent(options.backgroundOpacity);
303
+ }
304
+ nativeView.layer.cornerRadius = options.cornerRadius ?? 16;
305
+ // top corners only
306
+ nativeView.layer.maskedCorners = 1 /* CACornerMask.kCALayerMinXMinYCorner */ | 2 /* CACornerMask.kCALayerMaxXMinYCorner */;
307
+ nativeView.clipsToBounds = true;
308
+ }
309
+ _isInCatalystCard(view) {
310
+ const card = this.bottomSheetController?.catalystCard;
311
+ const nativeView = this.nativeViewProtected;
312
+ return !!view && ((nativeView instanceof UIView && view.isDescendantOfView(nativeView)) || (!!card?.grabberArea && view.isDescendantOfView(card.grabberArea)));
313
+ }
314
+ _shouldBeginCatalystPan(recognizer) {
315
+ const card = this.bottomSheetController?.catalystCard;
316
+ if (!card) {
317
+ return false;
318
+ }
319
+ if (recognizer.allowedScrollTypesMask) {
320
+ // the wheel: only to open the card
321
+ return card.index < card.detents.length - 1;
322
+ }
323
+ const velocity = recognizer.velocityInView(recognizer.view);
324
+ // the velocity is often still 0 here with a mouse: _onCatalystPan then leaves to the content what it must scroll
325
+ return velocity.x === 0 && velocity.y === 0 ? true : Math.abs(velocity.y) > Math.abs(velocity.x);
326
+ }
327
+ _findCatalystScrollView(recognizer) {
328
+ const cardView = recognizer.view;
329
+ let view = cardView.hitTestWithEvent(recognizer.locationInView(cardView), null);
330
+ while (view && view !== cardView) {
331
+ if (view instanceof UIScrollView) {
332
+ return view;
333
+ }
334
+ view = view.superview;
335
+ }
336
+ return null;
337
+ }
338
+ _snapCatalystCard(card, velocity) {
339
+ const toDip = (value) => Utils.layout.toDeviceIndependentPixels(value);
340
+ const heights = getCatalystDetentHeights(card).map(toDip);
341
+ const lowest = Math.min(...heights);
342
+ const projected = toDip(card.dragHeight ?? card.height) - velocity * 0.15;
343
+ if (projected < lowest * 0.6 && this._dismissCatalystCard()) {
344
+ return;
345
+ }
346
+ let index = 0;
347
+ heights.forEach((height, heightIndex) => {
348
+ if (Math.abs(height - projected) < Math.abs(heights[index] - projected)) {
349
+ index = heightIndex;
350
+ }
351
+ });
352
+ this._applyCatalystDetent(index, true);
353
+ }
354
+ /**
355
+ * Moves the card to a visible `height` (dip), from closed to the largest detent, without any layout
356
+ */
357
+ _dragCatalystCard(card, height) {
358
+ const toPx = (value) => Utils.layout.toDevicePixels(value);
359
+ const clamped = Math.min(card.height, Math.max(0, toPx(height)));
360
+ card.dragHeight = clamped;
361
+ const nativeView = this.nativeViewProtected;
362
+ if (nativeView instanceof UIView) {
363
+ setCatalystVisibleHeight(card, nativeView, clamped);
364
+ }
365
+ }
366
+ _onCatalystPan(recognizer) {
367
+ const card = this.bottomSheetController?.catalystCard;
368
+ if (!card) {
369
+ return;
370
+ }
371
+ const toDip = (value) => Utils.layout.toDeviceIndependentPixels(value);
372
+ if (recognizer.state === 1 /* UIGestureRecognizerState.Began */) {
373
+ const scrollView = this._findCatalystScrollView(recognizer);
374
+ this._catalystPan = { startHeight: toDip(card.dragHeight ?? this._visibleCatalystHeight(card)), scrollView, startOffsetY: scrollView ? scrollView.contentOffset.y : 0 };
375
+ return;
376
+ }
377
+ const pan = this._catalystPan;
378
+ if (!pan) {
379
+ return;
380
+ }
381
+ if (recognizer.state === 2 /* UIGestureRecognizerState.Changed */) {
382
+ const translation = recognizer.translationInView(recognizer.view).y;
383
+ const scrollView = pan.scrollView;
384
+ const maxHeight = toDip(card.height);
385
+ const currentHeight = toDip(card.dragHeight ?? this._visibleCatalystHeight(card));
386
+ const contentAtTop = !scrollView || scrollView.contentOffset.y <= -scrollView.adjustedContentInset.top + 1;
387
+ if ((translation < 0 && currentHeight >= maxHeight - 0.5) || (translation > 0 && !contentAtTop)) {
388
+ // the content scrolls: the card closes only once the content is back at its top
389
+ pan.startHeight = currentHeight;
390
+ recognizer.setTranslationInView(CGPointMake(0, 0), recognizer.view);
391
+ return;
392
+ }
393
+ this._dragCatalystCard(card, pan.startHeight - translation);
394
+ if (scrollView && toDip(card.dragHeight) < maxHeight) {
395
+ scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, pan.startOffsetY);
396
+ }
397
+ return;
398
+ }
399
+ this._catalystPan = null;
400
+ this._snapCatalystCard(card, recognizer.velocityInView(recognizer.view).y);
401
+ }
402
+ _onCatalystScroll(recognizer) {
403
+ const card = this.bottomSheetController?.catalystCard;
404
+ if (!card) {
405
+ return;
406
+ }
407
+ const toDip = (value) => Utils.layout.toDeviceIndependentPixels(value);
408
+ if (recognizer.state === 1 /* UIGestureRecognizerState.Began */) {
409
+ this._catalystPan = { startHeight: 0, scrollView: this._findCatalystScrollView(recognizer), startOffsetY: 0 };
410
+ this._catalystPan.startOffsetY = this._catalystPan.scrollView ? this._catalystPan.scrollView.contentOffset.y : 0;
411
+ return;
412
+ }
413
+ const pan = this._catalystPan;
414
+ if (!pan) {
415
+ return;
416
+ }
417
+ if (recognizer.state === 2 /* UIGestureRecognizerState.Changed */) {
418
+ const delta = recognizer.translationInView(recognizer.view).y;
419
+ recognizer.setTranslationInView(CGPointMake(0, 0), recognizer.view);
420
+ // content moving up = scrolling to its end: the card opens first
421
+ if (delta < 0 && toDip(card.dragHeight ?? this._visibleCatalystHeight(card)) < toDip(card.height) - 0.5) {
422
+ this._dragCatalystCard(card, toDip(card.dragHeight ?? this._visibleCatalystHeight(card)) - delta);
423
+ if (pan.scrollView) {
424
+ pan.scrollView.contentOffset = CGPointMake(pan.scrollView.contentOffset.x, pan.startOffsetY);
425
+ }
426
+ }
427
+ return;
428
+ }
429
+ this._catalystPan = null;
430
+ if (card.dragHeight !== undefined) {
431
+ this._snapCatalystCard(card, 0);
432
+ }
433
+ }
434
+ _visibleCatalystHeight(card) {
435
+ return getCatalystDetentHeights(card)[Math.min(card.index, card.detents.length - 1)] || card.height;
436
+ }
437
+ /**
438
+ * @returns false when the card cannot be dismissed (it then goes back to a detent)
439
+ */
440
+ _dismissCatalystCard() {
441
+ if (this._catalystOptions?.dismissOnDraggingDownSheet === false) {
442
+ return false;
443
+ }
444
+ if (this._onWillDismiss?.() === false) {
445
+ this._onDismissAttempt?.();
446
+ return false;
447
+ }
448
+ this.closeBottomSheet();
449
+ return true;
450
+ }
451
+ _applyCatalystDetent(index, animated) {
452
+ const controller = this.bottomSheetController;
453
+ const card = controller?.catalystCard;
454
+ if (!card) {
455
+ return;
456
+ }
457
+ const changed = index !== card.index;
458
+ card.index = index;
459
+ card.dragHeight = undefined;
460
+ const nativeView = this.nativeViewProtected;
461
+ const apply = () => {
462
+ if (nativeView instanceof UIView) {
463
+ setCatalystVisibleHeight(card, nativeView, this._visibleCatalystHeight(card));
464
+ }
465
+ };
466
+ if (animated) {
467
+ // a layout during the animation must not set the final position before it
468
+ card.animating = true;
469
+ UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.25, 0, 131072 /* UIViewAnimationOptions.CurveEaseOut */ | 4 /* UIViewAnimationOptions.BeginFromCurrentState */, apply, () => {
470
+ card.animating = false;
471
+ });
472
+ }
473
+ else {
474
+ apply();
475
+ }
476
+ if (changed) {
477
+ this._onChangeStateBottomSheetCallback?.(index === 0 ? StateBottomSheet.COLLAPSED : StateBottomSheet.EXPANDED);
478
+ this._onDetentChange?.(index);
479
+ }
480
+ }
481
+ _cycleCatalystDetent() {
482
+ const card = this.bottomSheetController?.catalystCard;
483
+ if (card && card.detents.length > 1) {
484
+ this.setBottomSheetDetentIndex((card.index + 1) % card.detents.length);
485
+ }
486
+ }
487
+ configureSheet(sheet, controller, options, parentController) {
488
+ const large = UISheetPresentationControllerDetent.largeDetent();
489
+ const detentIds = [];
490
+ this._onDetentChange = options.onDetentChange;
491
+ this._onWillDismiss = options.onWillDismiss;
492
+ this._onDismissAttempt = options.onDismissAttempt;
493
+ if (majorVersion >= 16) {
494
+ // the sheet follows the size of its content (measured in MDLayoutViewController)
495
+ const contentHeight = () => controller.preferredContentSize.height || Utils.layout.toDeviceIndependentPixels(0);
496
+ const detents = [];
497
+ const peekHeight = options.peekHeight;
498
+ if (options.detents?.length) {
499
+ options.detents.forEach((detent, index) => {
500
+ const id = `detent-${index}`;
501
+ detents.push(UISheetPresentationControllerDetent.customDetentWithIdentifierResolver(id, (context) => {
502
+ const max = context.maximumDetentValue;
503
+ let height;
504
+ if (detent === 'large') {
505
+ height = max;
506
+ }
507
+ else if (detent === 'medium') {
508
+ height = max / 2;
509
+ }
510
+ else if (detent === 'content') {
511
+ height = contentHeight() || max;
512
+ }
513
+ else if (typeof detent === 'number') {
514
+ height = detent;
515
+ }
516
+ else {
517
+ height = detent.fraction * max;
518
+ }
519
+ return Math.min(height, max);
520
+ }));
521
+ detentIds.push(id);
522
+ });
523
+ }
524
+ else if (peekHeight && options.skipCollapsedState !== true) {
525
+ detents.push(UISheetPresentationControllerDetent.customDetentWithIdentifierResolver(PEEK_DETENT, (context) => Math.min(peekHeight, context.maximumDetentValue)));
526
+ detentIds.push(PEEK_DETENT);
527
+ }
528
+ if (!options.detents?.length) {
529
+ detents.push(UISheetPresentationControllerDetent.customDetentWithIdentifierResolver(CONTENT_DETENT, (context) => {
530
+ const height = contentHeight();
531
+ return height > 0 ? Math.min(height, context.maximumDetentValue) : context.maximumDetentValue;
532
+ }));
533
+ detentIds.push(CONTENT_DETENT);
534
+ }
535
+ sheet.detents = NSArray.arrayWithArray(detents);
536
+ if (options.detents?.length) {
537
+ sheet.selectedDetentIdentifier = detentIds[Math.min(options.initialDetentIndex || 0, detentIds.length - 1)];
538
+ }
539
+ // update the detent once the content is measured
540
+ controller.onLayoutCallback = () => {
541
+ sheet.invalidateDetents();
542
+ };
543
+ }
544
+ else {
545
+ // iOS 15: only system detents
546
+ sheet.detents = NSArray.arrayWithArray([options.skipCollapsedState || !options.peekHeight ? large : UISheetPresentationControllerDetent.mediumDetent(), large]);
547
+ detentIds.push(LARGE_DETENT);
548
+ }
549
+ this._detentIds = detentIds;
550
+ sheet.prefersGrabberVisible = options.grabber !== false;
551
+ sheet.prefersScrollingExpandsWhenScrolledToEdge = options.scrollExpands !== false;
552
+ sheet.prefersEdgeAttachedInCompactHeight = options.edgeAttachedInCompactHeight === true;
553
+ sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true;
554
+ if (options.cornerRadius !== undefined) {
555
+ sheet.preferredCornerRadius = options.cornerRadius;
556
+ }
557
+ if (options.largestUndimmedDetentIndex !== undefined && detentIds[options.largestUndimmedDetentIndex]) {
558
+ sheet.largestUndimmedDetentIdentifier = detentIds[options.largestUndimmedDetentIndex];
559
+ if (options.canTouchBehind !== true && options.dismissOnBackgroundTap !== false) {
560
+ // tap outside dismisses, the touch is not given to the content behind
561
+ const view = parentController?.view;
562
+ if (view) {
563
+ this._outsideTapTarget = OutsideTapTarget.initWithOwner(this);
564
+ this._outsideTapRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(this._outsideTapTarget, 'outsideTapped');
565
+ this._outsideTapRecognizer.cancelsTouchesInView = true;
566
+ view.addGestureRecognizer(this._outsideTapRecognizer);
567
+ this._outsideTapView = view;
568
+ }
569
+ }
570
+ }
571
+ else if (options.canTouchBehind === true || options.disableDimBackground === true) {
572
+ // no dimming at the biggest detent => touches go through to the content behind
573
+ sheet.largestUndimmedDetentIdentifier = detentIds[detentIds.length - 1];
574
+ }
575
+ if (options.dismissOnBackgroundTap === false || options.dismissOnDraggingDownSheet === false) {
576
+ controller.modalInPresentation = true;
577
+ }
578
+ this.bottomSheetDelegate = SheetDelegateImpl.initWithOwner(this);
579
+ sheet.delegate = this.bottomSheetDelegate;
580
+ }
581
+ setBottomSheetDetentIndex(index, animated = true) {
582
+ const controller = this.bottomSheetController;
583
+ const card = controller?.catalystCard;
584
+ if (card) {
585
+ if (index >= 0 && index < card.detents.length) {
586
+ this._applyCatalystDetent(index, animated);
587
+ }
588
+ return;
589
+ }
590
+ const sheet = this.bottomSheetController?.sheetPresentationController;
591
+ const id = this._detentIds[index];
592
+ if (!sheet || !id) {
593
+ return;
594
+ }
595
+ const apply = () => {
596
+ sheet.selectedDetentIdentifier = id;
597
+ };
598
+ animated ? sheet.animateChanges(apply) : apply();
599
+ }
600
+ getBottomSheetDetentIndex() {
601
+ const card = this.bottomSheetController?.catalystCard;
602
+ if (card) {
603
+ return card.index;
604
+ }
605
+ const sheet = this.bottomSheetController?.sheetPresentationController;
606
+ return sheet ? this._detentIds.indexOf(sheet.selectedDetentIdentifier) : -1;
607
+ }
608
+ _bottomSheetClosed() {
609
+ // super already called we are just a mixin
610
+ if (this.bottomSheetController) {
611
+ const sheet = this.bottomSheetController.sheetPresentationController;
612
+ if (sheet) {
613
+ sheet.delegate = null;
614
+ }
615
+ this.bottomSheetController.onLayoutCallback = null;
616
+ this.bottomSheetController.catalystCard = null;
617
+ this.bottomSheetController = null;
618
+ }
619
+ this.bottomSheetDelegate = null;
620
+ this._catalystTarget = null;
621
+ this._catalystPan = null;
622
+ this._catalystOptions = null;
623
+ if (this._outsideTapRecognizer) {
624
+ this._outsideTapView?.removeGestureRecognizer(this._outsideTapRecognizer);
625
+ this._outsideTapRecognizer = null;
626
+ this._outsideTapTarget = null;
627
+ this._outsideTapView = null;
628
+ }
629
+ this._onDetentChange = null;
630
+ this._onWillDismiss = null;
631
+ this._onDismissAttempt = null;
632
+ // it is very important to clear the viewController as N does not do it
633
+ // and the destroy of the view from svelte could trigger a layout pass on the viewController
634
+ this.viewController = null;
635
+ }
636
+ _hideNativeBottomSheet(parent, whenClosedCallback) {
637
+ if (!this.viewController) {
638
+ // when dismissed by the user there is nothing to dismiss
639
+ whenClosedCallback?.();
640
+ return;
641
+ }
642
+ const parentController = getPresentingController(parent);
643
+ const animated = this.viewController.nsAnimated;
644
+ const controller = this.bottomSheetController;
645
+ const nativeView = this.nativeViewProtected;
646
+ const card = controller?.catalystCard;
647
+ if (card && animated && nativeView instanceof UIView) {
648
+ // slide out, the scrim fades along
649
+ const offset = CGAffineTransformMakeTranslation(0, Utils.layout.toDeviceIndependentPixels(card.height + 40));
650
+ // no layout may move the card back before it slides out
651
+ card.animating = true;
652
+ UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.25, 0, 65536 /* UIViewAnimationOptions.CurveEaseIn */ | 4 /* UIViewAnimationOptions.BeginFromCurrentState */, () => {
653
+ nativeView.transform = offset;
654
+ card.grabberArea && (card.grabberArea.transform = offset);
655
+ controller.view.backgroundColor = UIColor.clearColor;
656
+ }, () => parentController.dismissViewControllerAnimatedCompletion(false, whenClosedCallback));
657
+ return;
658
+ }
659
+ parentController.dismissViewControllerAnimatedCompletion(animated, whenClosedCallback);
660
+ }
661
+ _unloadBottomSheet() {
662
+ if (this.isLoaded) {
663
+ this.callUnloaded();
664
+ }
665
+ this._onDismissBottomSheetCallback && this._onDismissBottomSheetCallback();
666
+ }
667
+ }
668
+ let mixinInstalled = false;
669
+ export function overrideBottomSheet() {
670
+ applyMixins(View, [ViewWithBottomSheetBase, ViewWithBottomSheet]);
671
+ }
672
+ export function install() {
673
+ if (!mixinInstalled) {
674
+ mixinInstalled = true;
675
+ overrideBottomSheet();
676
+ }
677
+ }
678
+ //# sourceMappingURL=index.ios.js.map