Skip to content

Commit

Permalink
⭐️ Impl: Modal Events
Browse files Browse the repository at this point in the history
Summary:
* `TODO:2023-05-01-19-12-26` - Implement: `ModalView` events.
*  `TODO:2023-05-01-19-13-50` - Impl. `ModalVie.onModalDidSnap` event.
* `TODO:2023-04-22-05-29-40` - Impl. modal event `onModalSwipeGestureStart` + `onModalSwipeGestureDidEnd`.
  • Loading branch information
dominicstop committed May 3, 2023
1 parent 9bbc2fa commit 241af10
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 26 deletions.
9 changes: 9 additions & 0 deletions ios/src_library/React Native/RNIModal/RNIModalEventData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,12 @@ public struct RNIModalDetentDidComputeEventData: RNIDictionarySynthesizable {
public let computedDetentValue: CGFloat;
public let key: String;
};

public struct RNIModalSwipeGestureEventData: RNIDictionarySynthesizable {
public let position: CGPoint;
};

public struct RNIModalDidSnapEventData: RNIDictionarySynthesizable {
public let selectedDetentIdentifier: String?;
public let modalContentSize: CGSize;
};
83 changes: 66 additions & 17 deletions ios/src_library/React Native/RNIModalView/RNIModalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ public class RNIModalView:
@objc var onModalDetentDidCompute: RCTBubblingEventBlock?;
@objc var onModalDidChangeSelectedDetentIdentifier: RCTBubblingEventBlock?;

@objc var onModalDidSnap: RCTBubblingEventBlock?;

@objc var onModalSwipeGestureStart: RCTBubblingEventBlock?;
@objc var onModalSwipeGestureDidEnd: RCTBubblingEventBlock?;

// MARK: - Properties: React Props - General
// -----------------------------------------

Expand Down Expand Up @@ -497,6 +502,11 @@ public class RNIModalView:
return nil;
};

var currentSheetDetentString: String? {
guard #available(iOS 15.0, *) else { return nil };
return currentSheetDetentID?.rawValue;
};

var isModalViewPresentationNotificationEnabled: Bool {
RNIModalFlagsShared.isModalViewPresentationNotificationEnabled
};
Expand Down Expand Up @@ -619,6 +629,14 @@ public class RNIModalView:
self.presentingViewController = nil;
};

private func setupOnModalInitialPresent(){
guard let panGesture = self.modalGestureRecognizer else { return };

panGesture.addTarget(self,
action: #selector(Self.handleGestureRecognizer(_:))
);
};

private func notifyIfModalDismissCancelled(){
guard let modalVC = self.modalVC,
let transitionCoordinator = modalVC.transitionCoordinator
Expand All @@ -642,6 +660,49 @@ public class RNIModalView:
};
};

@objc private func handleGestureRecognizer(_ sender: UIGestureRecognizer) {
guard let window = self.window else { return };

switch sender.state {
case .began:
let gestureEventData = RNIModalSwipeGestureEventData(
position: sender.location(in: window)
);

self.onModalSwipeGestureStart?(
gestureEventData.synthesizedJSDictionary
);

case .ended:
let gestureEventData = RNIModalSwipeGestureEventData(
position: sender.location(in: window)
);

self.onModalSwipeGestureDidEnd?(
gestureEventData.synthesizedJSDictionary
);

guard let presentationController = self.modalVC?.presentationController,
let presentedView = presentationController.presentedView,
let positionAnimation =
presentedView.layer.animation(forKey: "position")
else { break };

positionAnimation.waitUntiEnd {
let eventData = RNIModalDidSnapEventData(
selectedDetentIdentifier: self.currentSheetDetentString,
modalContentSize: presentedView.bounds.size
);

self.onModalDidSnap?(
eventData.synthesizedJSDictionary
);
};

default: break;
};
};

/// `TODO:2023-03-22-12-07-54`
/// * Refactor: Move to `RNIModalManager`
///
Expand Down Expand Up @@ -808,17 +869,7 @@ public class RNIModalView:
);

completion?(true, nil);

// let panGesture = self.modalVC?
// .presentationController?
// .presentedView?
// .gestureRecognizers?
// .first {
// $0 is UIPanGestureRecognizer
// };
//
// panGesture?.addTarget(self, action: #selector(Self.handleGestureRecognizer(_:)))


#if DEBUG
print(
"Log - RNIModalView.presentModal - Present modal finished"
Expand All @@ -830,12 +881,6 @@ public class RNIModalView:
};
};

// @objc func handleGestureRecognizer(_ sender: UIPanGestureRecognizer) {
// print(
// "Test - handleGestureRecognizer - \(sender.state.description)"
// );
// };

public func dismissModal(completion: CompletionHandler? = nil) {
guard self.computedIsModalPresented else {
#if DEBUG
Expand Down Expand Up @@ -1132,6 +1177,10 @@ extension RNIModalView: RNIViewControllerLifeCycleNotifiable {
self.modalPresentationNotificationDelegate
.notifyOnModalWillShow(sender: self);
};

if self.modalPresentationState.isInitialPresent {
self.setupOnModalInitialPresent();
};
};

public func viewDidAppear(sender: UIViewController, animated: Bool) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ @interface RCT_EXTERN_MODULE(RNIModalViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(onModalDetentDidCompute, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onModalDidChangeSelectedDetentIdentifier, RCTBubblingEventBlock);

RCT_EXPORT_VIEW_PROPERTY(onModalDidSnap, RCTBubblingEventBlock);

RCT_EXPORT_VIEW_PROPERTY(onModalSwipeGestureStart, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onModalSwipeGestureDidEnd, RCTBubblingEventBlock);

// MARK: - Value Props - General
// -----------------------------

Expand Down
52 changes: 52 additions & 0 deletions src/components/ModalView/ModalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import {
RNIModalDeprecatedBaseEvent,
OnModalDetentDidComputeEvent,
OnModalDidChangeSelectedDetentIdentifierEvent,
OnModalDidSnapEvent,
OnModalSwipeGestureStartEvent,
OnModalSwipeGestureDidEndEvent,
} from '../../native_components/RNIModalView';

import { RNIModalViewModule } from '../../native_modules/RNIModalViewModule';
Expand Down Expand Up @@ -142,6 +145,9 @@ export class ModalView extends
onPresentationControllerDidAttemptToDismiss,
onModalDetentDidCompute,
onModalDidChangeSelectedDetentIdentifier,
onModalDidSnap,
onModalSwipeGestureStart,
onModalSwipeGestureDidEnd,

// Component Props
autoCloseOnUnmount,
Expand Down Expand Up @@ -228,6 +234,9 @@ export class ModalView extends
onPresentationControllerDidAttemptToDismiss,
onModalDetentDidCompute,
onModalDidChangeSelectedDetentIdentifier,
onModalDidSnap,
onModalSwipeGestureStart,
onModalSwipeGestureDidEnd,

// C - View-Related Props
children,
Expand Down Expand Up @@ -662,6 +671,46 @@ export class ModalView extends
);
};

private _handleOnModalDidSnap: OnModalDidSnapEvent = (event) => {
const props = this.props;

props.onModalDidSnap?.(event);
event.stopPropagation();

this.emitter.emit(
ModalViewEmitterEvents.onModalDidSnap,
event.nativeEvent
);
};

private _handleOnModalSwipeGestureStart:
OnModalSwipeGestureStartEvent = (event) => {

const props = this.props;

props.onModalSwipeGestureStart?.(event);
event.stopPropagation();

this.emitter.emit(
ModalViewEmitterEvents.onModalSwipeGestureStart,
event.nativeEvent
);
};

private _handleOnModalSwipeGestureDidEnd:
OnModalSwipeGestureDidEndEvent = (event) => {

const props = this.props;

props.onModalSwipeGestureDidEnd?.(event);
event.stopPropagation();

this.emitter.emit(
ModalViewEmitterEvents.onModalSwipeGestureDidEnd,
event.nativeEvent
);
};

private _renderModal() {
const { viewProps, ...props } = this.getProps();
const state = this.state;
Expand Down Expand Up @@ -708,6 +757,9 @@ export class ModalView extends
onPresentationControllerDidAttemptToDismiss={this._handleOnPresentationControllerDidAttemptToDismiss}
onModalDetentDidCompute={this._handleOnModalDetentDidCompute}
onModalDidChangeSelectedDetentIdentifier={this._handleOnModalDidChangeSelectedDetentIdentifier}
onModalDidSnap={this._handleOnModalDidSnap}
onModalSwipeGestureStart={this._handleOnModalSwipeGestureStart}
onModalSwipeGestureDidEnd={this._handleOnModalSwipeGestureDidEnd}
{...overrideProps}
{...viewProps}
>
Expand Down
9 changes: 9 additions & 0 deletions src/components/ModalView/ModalViewEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import type {
OnPresentationControllerDidAttemptToDismissEventObject,
OnModalDetentDidComputeEventObject,
OnModalDidChangeSelectedDetentIdentifierEventObject,
OnModalDidSnapEventObject,
OnModalSwipeGestureStartEventObject,
OnModalSwipeGestureDidEndEventObject,
} from 'src/native_components/RNIModalView';

import type { KeyMapType } from '../../types/UtilityTypes';
Expand Down Expand Up @@ -47,6 +50,9 @@ export enum ModalViewEmitterEvents {
onPresentationControllerDidAttemptToDismiss = 'onPresentationControllerDidAttemptToDismiss',
onModalDetentDidCompute = 'onModalDetentDidCompute',
onModalDidChangeSelectedDetentIdentifier = 'onModalDidChangeSelectedDetentIdentifier',
onModalDidSnap = 'onModalDidSnap',
onModalSwipeGestureStart = 'onModalSwipeGestureStart',
onModalSwipeGestureDidEnd = 'onModalSwipeGestureDidEnd',

onLayoutModalContentContainer = 'onLayoutModalContentContainer',
}
Expand Down Expand Up @@ -75,6 +81,9 @@ export type ModalViewEmitterEventMap =
onPresentationControllerDidAttemptToDismiss: OnPresentationControllerDidAttemptToDismissEventObject['nativeEvent'];
onModalDetentDidCompute: OnModalDetentDidComputeEventObject['nativeEvent'];
onModalDidChangeSelectedDetentIdentifier: OnModalDidChangeSelectedDetentIdentifierEventObject['nativeEvent'];
onModalDidSnap: OnModalDidSnapEventObject['nativeEvent'];
onModalSwipeGestureStart: OnModalSwipeGestureStartEventObject['nativeEvent'];
onModalSwipeGestureDidEnd: OnModalSwipeGestureDidEndEventObject['nativeEvent'];

onLayoutModalContentContainer: LayoutChangeEvent['nativeEvent'];
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/ModalView/ModalViewProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export type ModalViewBaseProps = Partial<
| 'onPresentationControllerDidAttemptToDismiss'
| 'onModalDetentDidCompute'
| 'onModalDidChangeSelectedDetentIdentifier'
| 'onModalDidSnap'
| 'onModalSwipeGestureStart'
| 'onModalSwipeGestureDidEnd'
>
> & {
modalContentPreferredContentSize?: RNIComputableSize;
Expand Down
46 changes: 37 additions & 9 deletions src/native_components/RNIModalView/RNIModalViewEvents.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { NativeSyntheticEvent } from 'react-native';
import type { CGPoint, CGSize } from 'src/types/NativeTypes';

import type {
ModalFocusState,
Expand All @@ -8,9 +9,7 @@ import type {
// Event Object Types
// ------------------

/**
* Based on `RNIModalData`
*/
/** Based on `RNIModalData` */
export type RNIModalData = {
modalNativeID: string;
modalIndex: number;
Expand All @@ -36,22 +35,29 @@ export type RNIModalData = {
synthesizedWindowID?: string;
};

/**
* Based on `RNIModalBaseEventData`
*/
/** Based on `RNIModalBaseEventData` */
export type RNIModalBaseEvent = RNIModalData & {
reactTag: number;
modalID?: string;
};

/**
* Based on `RNIOnModalFocusEventData`
*/
/** Based on `RNIOnModalFocusEventData` */
export type RNIOnModalFocusEvent = RNIModalBaseEvent & {
senderInfo: RNIModalData;
isInitial: boolean;
};

/** Based on `RNIModalSwipeGestureEventData` */
export type RNIModalSwipeGestureEvent = {
position: CGPoint;
};

/** Based on `RNIModalDidSnapEventData` */
export type RNIModalDidSnapEvent = {
selectedDetentIdentifier?: string;
modalContentSize: CGSize;
};

// Native Event Object
// -------------------

Expand Down Expand Up @@ -95,6 +101,7 @@ export type OnPresentationControllerDidAttemptToDismissEventObject =
export type OnModalWillFocusEventObject = NativeSyntheticEvent<
RNIOnModalFocusEvent & {}
>;

export type OnModalDidFocusEventObject = NativeSyntheticEvent<
RNIOnModalFocusEvent & {}
>;
Expand All @@ -118,6 +125,15 @@ export type OnModalDidBlurEventObject = NativeSyntheticEvent<
RNIOnModalFocusEvent & {}
>;

export type OnModalDidSnapEventObject =
NativeSyntheticEvent<RNIModalDidSnapEvent>;

export type OnModalSwipeGestureStartEventObject =
NativeSyntheticEvent<RNIModalSwipeGestureEvent>;

export type OnModalSwipeGestureDidEndEventObject =
NativeSyntheticEvent<RNIModalSwipeGestureEvent>;

// Event Handler Types
// -------------------

Expand Down Expand Up @@ -174,3 +190,15 @@ export type OnModalDetentDidComputeEvent = (
export type OnModalDidChangeSelectedDetentIdentifierEvent = (
event: OnModalDidChangeSelectedDetentIdentifierEventObject
) => void;

export type OnModalDidSnapEvent = (
event: OnModalDidSnapEventObject
) => void;

export type OnModalSwipeGestureStartEvent = (
event: OnModalSwipeGestureStartEventObject
) => void;

export type OnModalSwipeGestureDidEndEvent = (
event: OnModalSwipeGestureDidEndEventObject
) => void;
8 changes: 8 additions & 0 deletions src/native_components/RNIModalView/RNIModalViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import type {
OnPresentationControllerDidAttemptToDismissEvent,
OnModalDetentDidComputeEvent,
OnModalDidChangeSelectedDetentIdentifierEvent,
OnModalDidSnapEvent,
OnModalSwipeGestureStartEvent,
OnModalSwipeGestureDidEndEvent,
} from './RNIModalViewEvents';

import type { UnionWithAutoComplete } from 'src/types/UtilityTypes';
Expand Down Expand Up @@ -111,6 +114,11 @@ export type RNIModalViewBaseProps = {

onModalDetentDidCompute: OnModalDetentDidComputeEvent;
onModalDidChangeSelectedDetentIdentifier: OnModalDidChangeSelectedDetentIdentifierEvent;

onModalDidSnap: OnModalDidSnapEvent;

onModalSwipeGestureStart: OnModalSwipeGestureStartEvent;
onModalSwipeGestureDidEnd: OnModalSwipeGestureDidEndEvent;
};

export type RNIModalViewProps = Partial<ViewProps> & RNIModalViewBaseProps;
Expand Down

0 comments on commit 241af10

Please sign in to comment.