Skip to content

Commit 83dd646

Browse files
authored
Merge pull request #332 from vallemar/master
feat: BottomSheet add callback onChangeState
2 parents 14612cf + af24ee4 commit 83dd646

File tree

3 files changed

+119
-12
lines changed

3 files changed

+119
-12
lines changed

src/bottomsheet/bottomsheet-common.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ export interface ShownBottomSheetData extends EventData {
1414
closeCallback?: Function;
1515
}
1616

17+
export enum StateBottomSheet {
18+
CLOSED = -1,
19+
COLLAPSED = 0,
20+
EXPANDED = 1,
21+
DRAGGING = 2
22+
}
23+
24+
export type onChangeStateBottomSheet = (stateBottomSheet: StateBottomSheet, slideOffset?: number) => void;
25+
1726
export const shownInBottomSheetEvent = 'shownInBottomSheet';
1827
export const showingInBottomSheetEvent = 'showingInBottomSheet';
1928

@@ -33,6 +42,7 @@ export interface BottomSheetOptions {
3342
skipCollapsedState?: boolean; // optional Android parameter to skip midway state when view is greater than 50%. Default is false
3443
peekHeight?: number; // optional parameter to set the collapsed sheet height. To work on iOS you need to set trackingScrollView.
3544
ignoreKeyboardHeight: boolean; //(iOS only) A Boolean value that controls whether the height of the keyboard should affect the bottom sheet's frame when the keyboard shows on the screen. (Default: true)
45+
onChangeState?: onChangeStateBottomSheet; // One works to be called on the scroll of the sheet. Parameters: state (CLOSED, DRAGGING, DRAGGING, COLLAPSED) and slideOffset is the new offset of this bottom sheet within [-1,1] range. Offset increases as this bottom sheet is moving upward. From 0 to 1 the sheet is between collapsed and expanded states and from -1 to 0 it is between hidden and collapsed states.
3646
}
3747

3848
export abstract class ViewWithBottomSheetBase extends View {
@@ -42,9 +52,14 @@ export abstract class ViewWithBottomSheetBase extends View {
4252
// used when the bottomSheet is dismissed
4353
public _onDismissBottomSheetCallback: Function;
4454

55+
// used when the bottomSheet change state
56+
public _onChangeStateBottomSheetCallback: onChangeStateBottomSheet;
57+
4558
_bottomSheetFragment: any; // com.google.android.material.bottomsheet.BottomSheetDialogFragment
4659
protected abstract _hideNativeBottomSheet(parent, whenClosedCallback);
60+
4761
protected _bottomSheetContext: any;
62+
4863
_raiseShownBottomSheetEvent() {
4964
const args: ShownBottomSheetData = {
5065
eventName: shownInBottomSheetEvent,
@@ -55,6 +70,7 @@ export abstract class ViewWithBottomSheetBase extends View {
5570

5671
this.notify(args);
5772
}
73+
5874
public _bottomSheetClosed(): void {
5975
const _rootModalViews = this._getRootModalViews();
6076
const modalIndex = _rootModalViews.indexOf(this);
@@ -69,7 +85,9 @@ export abstract class ViewWithBottomSheetBase extends View {
6985
// return true;
7086
// });
7187
}
88+
7289
protected abstract _showNativeBottomSheet(parent: View, options: BottomSheetOptions);
90+
7391
protected _commonShowNativeBottomSheet(parent: View, options: BottomSheetOptions) {
7492
this._getRootModalViews().push(this);
7593
this.cssClasses.add(CSSUtils.MODAL_ROOT_VIEW_CSS_CLASS);
@@ -111,8 +129,16 @@ export abstract class ViewWithBottomSheetBase extends View {
111129
};
112130
this._hideNativeBottomSheet(parent, whenClosedCallback);
113131
};
132+
133+
if (options.onChangeState && typeof options.onChangeState === 'function') {
134+
this._onChangeStateBottomSheetCallback = (stateBottomSheet: StateBottomSheet, slideOffset: number) => {
135+
options.onChangeState(stateBottomSheet, slideOffset ?? stateBottomSheet);
136+
};
137+
}
138+
114139
this._bottomSheetContext.closeCallback = this._closeBottomSheetCallback;
115140
}
141+
116142
protected _raiseShowingBottomSheetEvent() {
117143
const args: ShownBottomSheetData = {
118144
eventName: showingInBottomSheetEvent,
@@ -122,6 +148,7 @@ export abstract class ViewWithBottomSheetBase extends View {
122148
};
123149
this.notify(args);
124150
}
151+
125152
public closeBottomSheet(...args) {
126153
const closeCallback = this._closeBottomSheetCallback;
127154
if (closeCallback) {

src/bottomsheet/bottomsheet.android.ts

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { applyMixins } from '@nativescript-community/ui-material-core';
2-
import { AndroidActivityBackPressedEventData, Application, View, fromObject, Utils } from '@nativescript/core';
3-
import { BottomSheetOptions, ViewWithBottomSheetBase } from './bottomsheet-common';
2+
import { AndroidActivityBackPressedEventData, Application, Utils, View, fromObject } from '@nativescript/core';
3+
import { BottomSheetOptions, StateBottomSheet, ViewWithBottomSheetBase } from './bottomsheet-common';
44

55
export { ViewWithBottomSheetBase } from './bottomsheet-common';
66

@@ -16,6 +16,52 @@ function getId(id: string) {
1616
return context.getResources().getIdentifier(id, 'id', context.getPackageName());
1717
}
1818

19+
@NativeClass
20+
class ChangeStateBottomSheet extends com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback {
21+
private owner: ViewWithBottomSheet;
22+
23+
constructor(owner: ViewWithBottomSheet) {
24+
super();
25+
this.owner = owner;
26+
return global.__native(this);
27+
}
28+
29+
onSlide(bottomSheet: android.view.View, slideOffset: number) {
30+
if (this.checkActiveCallback()) {
31+
this.owner._onChangeStateBottomSheetCallback(StateBottomSheet.DRAGGING, slideOffset);
32+
}
33+
}
34+
35+
onStateChanged(bottomSheet: android.view.View, newState: number) {
36+
if (this.checkActiveCallback()) {
37+
const status = this.getStateBottomSheetFromBottomSheetBehavior(newState);
38+
if (status !== undefined) {
39+
this.owner._onChangeStateBottomSheetCallback(status);
40+
}
41+
}
42+
}
43+
44+
private checkActiveCallback() {
45+
return this.owner && this.owner._onChangeStateBottomSheetCallback;
46+
}
47+
48+
public getStateBottomSheetFromBottomSheetBehavior(state: number): StateBottomSheet | undefined {
49+
switch (state) {
50+
case com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN:
51+
return StateBottomSheet.CLOSED;
52+
case com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED:
53+
return StateBottomSheet.EXPANDED;
54+
case com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED:
55+
return StateBottomSheet.COLLAPSED;
56+
case com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING:
57+
return StateBottomSheet.DRAGGING;
58+
default:
59+
// @ts-ignore
60+
return;
61+
}
62+
}
63+
}
64+
1965
export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
2066
_bottomSheetFragment: com.nativescript.material.bottomsheet.BottomSheetDialogFragment;
2167
protected _hideNativeBottomSheet(parent: View, whenClosedCallback: () => void) {
@@ -51,10 +97,10 @@ export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
5197
},
5298

5399
onBackPressed(dialog: com.nativescript.material.bottomsheet.BottomSheetDialog) {
54-
if(bottomSheetOptions.options && bottomSheetOptions.options.dismissOnBackButton === false){
100+
if (bottomSheetOptions.options && bottomSheetOptions.options.dismissOnBackButton === false) {
55101
return true;
56102
}
57-
103+
58104
if (!owner) {
59105
return false;
60106
}
@@ -133,13 +179,19 @@ export class ViewWithBottomSheet extends ViewWithBottomSheetBase {
133179
// disable peek/collapsed state
134180
behavior.setSkipCollapsed(true);
135181
}
136-
137182
const peekHeight = bottomSheetOptions.options?.peekHeight;
138-
if(peekHeight){
183+
if (peekHeight) {
139184
behavior.setState(com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED);
140185
behavior.setPeekHeight(Utils.layout.toDevicePixels(peekHeight));
141186
}
142187

188+
const onChangeState = bottomSheetOptions.options?.onChangeState;
189+
if (onChangeState) {
190+
const changeStateBottomSheet = new ChangeStateBottomSheet(owner);
191+
behavior.addBottomSheetCallback(changeStateBottomSheet);
192+
owner._onChangeStateBottomSheetCallback(changeStateBottomSheet.getStateBottomSheetFromBottomSheetBehavior(behavior.getState()));
193+
}
194+
143195
if (owner && !owner.isLoaded) {
144196
owner.callLoaded();
145197
}

src/bottomsheet/bottomsheet.ios.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { Application, IOSHelper, Page, Screen, Trace, Utils, View, fromObject } from '@nativescript/core';
2-
import { iOSNativeHelper } from '@nativescript/core/utils';
3-
import { iosIgnoreSafeAreaProperty } from '@nativescript/core/ui/core/view/view-common';
42
import { applyMixins } from '@nativescript-community/ui-material-core';
53
import { BottomSheetOptions } from './bottomsheet';
6-
import { ViewWithBottomSheetBase } from './bottomsheet-common';
4+
import { StateBottomSheet, ViewWithBottomSheetBase } from './bottomsheet-common';
75

86
export { ViewWithBottomSheetBase } from './bottomsheet-common';
97

@@ -24,7 +22,32 @@ class MDCBottomSheetControllerDelegateImpl extends NSObject {
2422

2523
return delegate;
2624
}
27-
25+
bottomSheetControllerDidChangeYOffsetYOffset(controller: MDCBottomSheetController, yOffset: number) {
26+
const owner = this._owner.get();
27+
if (owner && owner._onChangeStateBottomSheetCallback) {
28+
const presentationController = controller.presentationController as MDCBottomSheetPresentationController;
29+
const heightScreen = Screen.mainScreen.heightDIPs;
30+
const heightCollapsedSheet = presentationController.preferredSheetHeight || controller.preferredContentSize.height;
31+
const window = UIApplication.sharedApplication.windows.firstObject;
32+
const topPadding = window.safeAreaInsets.top;
33+
const bottomPadding = window.safeAreaInsets.bottom;
34+
35+
const isCollapsed = heightScreen - yOffset - bottomPadding === heightCollapsedSheet;
36+
const isExpanded = yOffset === topPadding;
37+
if (!isCollapsed && !isExpanded) {
38+
//normalized = (value - min) / (max - min);
39+
let normalized = 0;
40+
if (yOffset + bottomPadding > heightScreen - heightCollapsedSheet) {
41+
normalized = ((heightScreen - yOffset - bottomPadding) - 0) / (heightCollapsedSheet - 0) - 1;
42+
} else {
43+
normalized = ((heightScreen - yOffset) - heightCollapsedSheet) / (heightScreen - heightCollapsedSheet);
44+
}
45+
owner._onChangeStateBottomSheetCallback(StateBottomSheet.DRAGGING, normalized);
46+
} else {
47+
owner._onChangeStateBottomSheetCallback(isCollapsed ? StateBottomSheet.COLLAPSED : StateBottomSheet.EXPANDED);
48+
}
49+
}
50+
}
2851
bottomSheetControllerDidDismissBottomSheet(controller: MDCBottomSheetController) {
2952
// called when clicked on background
3053
const owner = this._owner.get();
@@ -37,14 +60,19 @@ class MDCBottomSheetControllerDelegateImpl extends NSObject {
3760
}
3861
bottomSheetControllerStateChangedState(controller: MDCBottomSheetController, state: MDCSheetState) {
3962
// called when swiped
63+
const owner = this._owner.get();
4064
if (state === MDCSheetState.Closed) {
41-
const owner = this._owner.get();
4265
if (owner) {
4366
owner._onDismissBottomSheetCallback && owner._onDismissBottomSheetCallback();
67+
owner._onChangeStateBottomSheetCallback && owner._onChangeStateBottomSheetCallback(StateBottomSheet.CLOSED);
4468
if (owner && owner.isLoaded) {
4569
owner.callUnloaded();
4670
}
4771
}
72+
} else {
73+
if (owner && owner._onChangeStateBottomSheetCallback) {
74+
owner._onChangeStateBottomSheetCallback(state === MDCSheetState.Extended ? StateBottomSheet.EXPANDED : StateBottomSheet.EXPANDED)
75+
}
4876
}
4977
}
5078
}
@@ -471,4 +499,4 @@ export function install() {
471499
mixinInstalled = true;
472500
overrideBottomSheet();
473501
}
474-
}
502+
}

0 commit comments

Comments
 (0)