@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/CHANGELOG.md +22 -0
- package/LICENSE +201 -0
- package/README.md +213 -2
- package/index-common.d.ts +93 -0
- package/index-common.js +140 -0
- package/index-common.js.map +1 -0
- package/index.android.d.ts +44 -0
- package/index.android.js +475 -0
- package/index.android.js.map +1 -0
- package/index.d.ts +100 -0
- package/index.ios.d.ts +114 -0
- package/index.ios.js +678 -0
- package/index.ios.js.map +1 -0
- package/ios-layout.d.ts +64 -0
- package/ios-layout.js +356 -0
- package/ios-layout.js.map +1 -0
- package/package.json +49 -5
- package/platforms/android/include.gradle +6 -0
- package/platforms/android/java/com/nativescript/ui/bottomsheet/BottomSheetDialog.java +38 -0
- package/platforms/android/java/com/nativescript/ui/bottomsheet/BottomSheetDialogFragment.java +73 -0
- package/platforms/android/native-api-usage.json +36 -0
- package/platforms/ios/native-api-usage.json +11 -0
- package/svelte/index.d.ts +24 -0
- package/svelte/index.js +48 -0
- package/svelte/index.js.map +1 -0
- package/tsconfig.tsbuildinfo +1 -0
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
|