react-native-nitro-modal 0.3.1-rc → 0.3.4-rc
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/README.md +4 -1
- package/android/src/main/java/com/margelo/nitro/nitromodal/BottomSheetPresenter.kt +29 -15
- package/android/src/main/java/com/margelo/nitro/nitromodal/HybridNitroModal.kt +5 -0
- package/android/src/main/java/com/margelo/nitro/nitromodal/ModalConfig.kt +3 -0
- package/android/src/main/java/com/margelo/nitro/nitromodal/PopupPresenter.kt +6 -0
- package/ios/HybridNitroModal.swift +4 -0
- package/ios/ModalConfig.swift +3 -0
- package/ios/PopupViewController.swift +2 -0
- package/ios/SheetView.swift +56 -21
- package/lib/module/NitroModal.js +4 -0
- package/lib/module/NitroModal.js.map +1 -1
- package/lib/module/NitroModalView.web.js +24 -8
- package/lib/module/NitroModalView.web.js.map +1 -1
- package/lib/module/web/ModalConfig.js +2 -0
- package/lib/module/web/ModalConfig.js.map +1 -1
- package/lib/module/web/ModalPresenter.js +5 -0
- package/lib/module/web/ModalPresenter.js.map +1 -1
- package/lib/module/web/SheetPresenter.js +50 -11
- package/lib/module/web/SheetPresenter.js.map +1 -1
- package/lib/typescript/src/NitroModal.d.ts +8 -1
- package/lib/typescript/src/NitroModal.d.ts.map +1 -1
- package/lib/typescript/src/NitroModal.nitro.d.ts +4 -0
- package/lib/typescript/src/NitroModal.nitro.d.ts.map +1 -1
- package/lib/typescript/src/NitroModalView.web.d.ts.map +1 -1
- package/lib/typescript/src/web/ModalConfig.d.ts +4 -0
- package/lib/typescript/src/web/ModalConfig.d.ts.map +1 -1
- package/lib/typescript/src/web/ModalPresenter.d.ts +3 -1
- package/lib/typescript/src/web/ModalPresenter.d.ts.map +1 -1
- package/lib/typescript/src/web/SheetPresenter.d.ts +14 -2
- package/lib/typescript/src/web/SheetPresenter.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridNitroModalSpec.cpp +18 -0
- package/nitrogen/generated/android/c++/JHybridNitroModalSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/views/JHybridNitroModalStateUpdater.cpp +10 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromodal/HybridNitroModalSpec.kt +12 -0
- package/nitrogen/generated/ios/c++/HybridNitroModalSpecSwift.hpp +13 -0
- package/nitrogen/generated/ios/c++/views/HybridNitroModalComponent.mm +12 -0
- package/nitrogen/generated/ios/swift/HybridNitroModalSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNitroModalSpec_cxx.swift +35 -0
- package/nitrogen/generated/shared/c++/HybridNitroModalSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridNitroModalSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNitroModalComponent.cpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNitroModalComponent.hpp +6 -0
- package/nitrogen/generated/shared/json/NitroModalConfig.json +2 -0
- package/package.json +1 -1
- package/src/NitroModal.nitro.ts +4 -0
- package/src/NitroModal.tsx +11 -0
- package/src/NitroModalView.web.tsx +16 -3
- package/src/web/ModalConfig.ts +9 -0
- package/src/web/ModalPresenter.ts +9 -1
- package/src/web/SheetPresenter.ts +39 -13
|
@@ -90,6 +90,10 @@ namespace margelo::nitro::nitromodal {
|
|
|
90
90
|
virtual void setGrabberVisible(bool grabberVisible) = 0;
|
|
91
91
|
virtual double getCornerRadius() = 0;
|
|
92
92
|
virtual void setCornerRadius(double cornerRadius) = 0;
|
|
93
|
+
virtual double getBorderWidth() = 0;
|
|
94
|
+
virtual void setBorderWidth(double borderWidth) = 0;
|
|
95
|
+
virtual std::optional<double> getBorderColor() = 0;
|
|
96
|
+
virtual void setBorderColor(std::optional<double> borderColor) = 0;
|
|
93
97
|
virtual std::optional<double> getContentBackgroundColor() = 0;
|
|
94
98
|
virtual void setContentBackgroundColor(std::optional<double> contentBackgroundColor) = 0;
|
|
95
99
|
virtual KeyboardBehavior getKeyboardBehavior() = 0;
|
|
@@ -33,6 +33,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
33
33
|
dismissOnBackButton(nitro::ReactProp<bool>::fromRawValue("NitroModal", "dismissOnBackButton", rawProps, sourceProps.dismissOnBackButton)),
|
|
34
34
|
grabberVisible(nitro::ReactProp<bool>::fromRawValue("NitroModal", "grabberVisible", rawProps, sourceProps.grabberVisible)),
|
|
35
35
|
cornerRadius(nitro::ReactProp<double>::fromRawValue("NitroModal", "cornerRadius", rawProps, sourceProps.cornerRadius)),
|
|
36
|
+
borderWidth(nitro::ReactProp<double>::fromRawValue("NitroModal", "borderWidth", rawProps, sourceProps.borderWidth)),
|
|
37
|
+
borderColor(nitro::ReactProp<std::optional<double>>::fromRawValue("NitroModal", "borderColor", rawProps, sourceProps.borderColor)),
|
|
36
38
|
contentBackgroundColor(nitro::ReactProp<std::optional<double>>::fromRawValue("NitroModal", "contentBackgroundColor", rawProps, sourceProps.contentBackgroundColor)),
|
|
37
39
|
keyboardBehavior(nitro::ReactProp<KeyboardBehavior>::fromRawValue("NitroModal", "keyboardBehavior", rawProps, sourceProps.keyboardBehavior)),
|
|
38
40
|
popupAnimation(nitro::ReactProp<PopupAnimation>::fromRawValue("NitroModal", "popupAnimation", rawProps, sourceProps.popupAnimation)),
|
|
@@ -64,6 +66,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
64
66
|
case hashString("dismissOnBackButton"): return true;
|
|
65
67
|
case hashString("grabberVisible"): return true;
|
|
66
68
|
case hashString("cornerRadius"): return true;
|
|
69
|
+
case hashString("borderWidth"): return true;
|
|
70
|
+
case hashString("borderColor"): return true;
|
|
67
71
|
case hashString("contentBackgroundColor"): return true;
|
|
68
72
|
case hashString("keyboardBehavior"): return true;
|
|
69
73
|
case hashString("popupAnimation"): return true;
|
|
@@ -63,6 +63,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
63
63
|
nitro::ReactProp<bool> dismissOnBackButton;
|
|
64
64
|
nitro::ReactProp<bool> grabberVisible;
|
|
65
65
|
nitro::ReactProp<double> cornerRadius;
|
|
66
|
+
nitro::ReactProp<double> borderWidth;
|
|
67
|
+
nitro::ReactProp<std::optional<double>> borderColor;
|
|
66
68
|
nitro::ReactProp<std::optional<double>> contentBackgroundColor;
|
|
67
69
|
nitro::ReactProp<KeyboardBehavior> keyboardBehavior;
|
|
68
70
|
nitro::ReactProp<PopupAnimation> popupAnimation;
|
|
@@ -94,6 +96,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
94
96
|
dismissOnBackButton.hasSameValue(other.dismissOnBackButton) &&
|
|
95
97
|
grabberVisible.hasSameValue(other.grabberVisible) &&
|
|
96
98
|
cornerRadius.hasSameValue(other.cornerRadius) &&
|
|
99
|
+
borderWidth.hasSameValue(other.borderWidth) &&
|
|
100
|
+
borderColor.hasSameValue(other.borderColor) &&
|
|
97
101
|
contentBackgroundColor.hasSameValue(other.contentBackgroundColor) &&
|
|
98
102
|
keyboardBehavior.hasSameValue(other.keyboardBehavior) &&
|
|
99
103
|
popupAnimation.hasSameValue(other.popupAnimation) &&
|
|
@@ -126,6 +130,8 @@ namespace margelo::nitro::nitromodal::views {
|
|
|
126
130
|
dismissOnBackButton.isProvided() ||
|
|
127
131
|
grabberVisible.isProvided() ||
|
|
128
132
|
cornerRadius.isProvided() ||
|
|
133
|
+
borderWidth.isProvided() ||
|
|
134
|
+
borderColor.isProvided() ||
|
|
129
135
|
contentBackgroundColor.isProvided() ||
|
|
130
136
|
keyboardBehavior.isProvided() ||
|
|
131
137
|
popupAnimation.isProvided() ||
|
package/package.json
CHANGED
package/src/NitroModal.nitro.ts
CHANGED
|
@@ -77,6 +77,10 @@ export interface NitroModalProps extends HybridViewProps {
|
|
|
77
77
|
grabberVisible: boolean;
|
|
78
78
|
/** Corner radius of the sheet/card. A negative value uses the platform default. */
|
|
79
79
|
cornerRadius: number;
|
|
80
|
+
/** Width (dp/pt) of the border drawn around the sheet/card, above the content. 0 draws none. */
|
|
81
|
+
borderWidth: number;
|
|
82
|
+
/** Border color as a processed ARGB color. */
|
|
83
|
+
borderColor?: number;
|
|
80
84
|
/** Sheet/card background as a processed ARGB color. Omit for the system background. */
|
|
81
85
|
contentBackgroundColor?: number;
|
|
82
86
|
keyboardBehavior: KeyboardBehavior;
|
package/src/NitroModal.tsx
CHANGED
|
@@ -92,6 +92,13 @@ export interface NitroModalProps {
|
|
|
92
92
|
showGrabber?: boolean;
|
|
93
93
|
/** Corner radius of the sheet/card. Defaults to the platform style. */
|
|
94
94
|
cornerRadius?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Width (dp/pt/px) of a border around the sheet/card, drawn above the
|
|
97
|
+
* content and following its corners. @default 0
|
|
98
|
+
*/
|
|
99
|
+
borderWidth?: number;
|
|
100
|
+
/** Color of the border. @default 'transparent' */
|
|
101
|
+
borderColor?: ColorValue;
|
|
95
102
|
/** Sheet/card background. Defaults to the system surface color. */
|
|
96
103
|
backgroundColor?: ColorValue;
|
|
97
104
|
/** @default 'pan' */
|
|
@@ -158,6 +165,8 @@ export function NitroModal({
|
|
|
158
165
|
dismissOnBackButton = true,
|
|
159
166
|
showGrabber = false,
|
|
160
167
|
cornerRadius,
|
|
168
|
+
borderWidth = 0,
|
|
169
|
+
borderColor,
|
|
161
170
|
backgroundColor,
|
|
162
171
|
keyboardBehavior = 'pan',
|
|
163
172
|
popupAnimation = 'scale',
|
|
@@ -334,6 +343,8 @@ export function NitroModal({
|
|
|
334
343
|
dismissOnBackButton={dismissOnBackButton}
|
|
335
344
|
grabberVisible={showGrabber}
|
|
336
345
|
cornerRadius={cornerRadius ?? -1}
|
|
346
|
+
borderWidth={borderWidth}
|
|
347
|
+
borderColor={toNativeColor(borderColor)}
|
|
337
348
|
contentBackgroundColor={toNativeColor(backgroundColor)}
|
|
338
349
|
keyboardBehavior={keyboardBehavior}
|
|
339
350
|
popupAnimation={popupAnimation}
|
|
@@ -45,6 +45,7 @@ function WebNitroModalView(props: WebNitroModalViewProps) {
|
|
|
45
45
|
const surface = useRef<HTMLDivElement>(null);
|
|
46
46
|
const content = useRef<HTMLDivElement>(null);
|
|
47
47
|
const grabber = useRef<HTMLDivElement>(null);
|
|
48
|
+
const border = useRef<HTMLDivElement>(null);
|
|
48
49
|
const refreshIndicator = useRef<HTMLDivElement>(null);
|
|
49
50
|
const refreshSpinner = useRef<HTMLDivElement>(null);
|
|
50
51
|
|
|
@@ -69,6 +70,7 @@ function WebNitroModalView(props: WebNitroModalViewProps) {
|
|
|
69
70
|
surface: surface.current!,
|
|
70
71
|
content: content.current!,
|
|
71
72
|
grabber: grabber.current!,
|
|
73
|
+
border: border.current!,
|
|
72
74
|
refreshIndicator: refreshIndicator.current!,
|
|
73
75
|
refreshSpinner: refreshSpinner.current!,
|
|
74
76
|
});
|
|
@@ -102,14 +104,15 @@ function WebNitroModalView(props: WebNitroModalViewProps) {
|
|
|
102
104
|
}
|
|
103
105
|
>
|
|
104
106
|
<div ref={backdrop} aria-hidden style={styles.backdrop} />
|
|
105
|
-
<div ref={refreshIndicator} aria-hidden style={styles.refreshIndicator}>
|
|
106
|
-
<div ref={refreshSpinner} style={styles.refreshSpinner} />
|
|
107
|
-
</div>
|
|
108
107
|
<div ref={surface} style={styles.surface}>
|
|
108
|
+
<div ref={refreshIndicator} aria-hidden style={styles.refreshIndicator}>
|
|
109
|
+
<div ref={refreshSpinner} style={styles.refreshSpinner} />
|
|
110
|
+
</div>
|
|
109
111
|
<div ref={content} style={styles.content}>
|
|
110
112
|
{children}
|
|
111
113
|
</div>
|
|
112
114
|
<div ref={grabber} aria-hidden style={styles.grabber} />
|
|
115
|
+
<div ref={border} aria-hidden style={styles.border} />
|
|
113
116
|
</div>
|
|
114
117
|
</div>
|
|
115
118
|
) : null;
|
|
@@ -179,6 +182,16 @@ const styles = {
|
|
|
179
182
|
backgroundColor: 'rgba(127, 127, 127, 0.4)',
|
|
180
183
|
pointerEvents: 'none',
|
|
181
184
|
},
|
|
185
|
+
// Sized by the presenter's border width; follows the surface's corners.
|
|
186
|
+
border: {
|
|
187
|
+
position: 'absolute',
|
|
188
|
+
inset: 0,
|
|
189
|
+
boxSizing: 'border-box',
|
|
190
|
+
borderStyle: 'solid',
|
|
191
|
+
borderWidth: 0,
|
|
192
|
+
borderRadius: 'inherit',
|
|
193
|
+
pointerEvents: 'none',
|
|
194
|
+
},
|
|
182
195
|
refreshIndicator: {
|
|
183
196
|
position: 'absolute',
|
|
184
197
|
left: 0,
|
package/src/web/ModalConfig.ts
CHANGED
|
@@ -26,6 +26,10 @@ export interface ModalConfig {
|
|
|
26
26
|
grabberVisible: boolean;
|
|
27
27
|
/** `null` uses the default. */
|
|
28
28
|
cornerRadius: number | null;
|
|
29
|
+
/** Border around the surface, drawn above the content. */
|
|
30
|
+
borderWidth: number;
|
|
31
|
+
/** 0xAARRGGBB; `null` draws no border. */
|
|
32
|
+
borderColor: number | null;
|
|
29
33
|
/** 0xAARRGGBB; `null` uses the system background. */
|
|
30
34
|
contentBackgroundColor: number | null;
|
|
31
35
|
keyboardBehavior: KeyboardBehavior;
|
|
@@ -55,6 +59,11 @@ export function makeConfig(props: NitroModalProps): ModalConfig {
|
|
|
55
59
|
dismissOnBackButton: props.dismissOnBackButton && !isInline,
|
|
56
60
|
grabberVisible: props.grabberVisible,
|
|
57
61
|
cornerRadius: props.cornerRadius >= 0 ? props.cornerRadius : null,
|
|
62
|
+
borderWidth:
|
|
63
|
+
Number.isFinite(props.borderWidth) && props.borderWidth > 0
|
|
64
|
+
? props.borderWidth
|
|
65
|
+
: 0,
|
|
66
|
+
borderColor: props.borderColor ?? null,
|
|
58
67
|
contentBackgroundColor: props.contentBackgroundColor ?? null,
|
|
59
68
|
keyboardBehavior: props.keyboardBehavior,
|
|
60
69
|
popupAnimation: props.popupAnimation,
|
|
@@ -23,7 +23,9 @@ export interface ModalElements {
|
|
|
23
23
|
/** Hosts the React content. */
|
|
24
24
|
content: HTMLElement;
|
|
25
25
|
grabber: HTMLElement;
|
|
26
|
-
/**
|
|
26
|
+
/** Above the content, following the surface's corners. */
|
|
27
|
+
border: HTMLElement;
|
|
28
|
+
/** Inside the sheet behind the content, uncovered as it's pulled down to refresh. */
|
|
27
29
|
refreshIndicator: HTMLElement;
|
|
28
30
|
/** The spinning ring inside `refreshIndicator`. */
|
|
29
31
|
refreshSpinner: HTMLElement;
|
|
@@ -246,6 +248,12 @@ export abstract class ModalPresenter {
|
|
|
246
248
|
config.contentBackgroundColor != null
|
|
247
249
|
? toCSSColor(config.contentBackgroundColor)
|
|
248
250
|
: 'Canvas';
|
|
251
|
+
const { border } = this.elements!;
|
|
252
|
+
border.style.borderWidth = `${config.borderWidth}px`;
|
|
253
|
+
border.style.borderColor =
|
|
254
|
+
config.borderColor != null
|
|
255
|
+
? toCSSColor(config.borderColor)
|
|
256
|
+
: 'transparent';
|
|
249
257
|
}
|
|
250
258
|
|
|
251
259
|
/** Inline, only the surface takes input; the rest reaches the page. */
|
|
@@ -339,28 +339,42 @@ export class SheetPresenter extends ModalPresenter {
|
|
|
339
339
|
|
|
340
340
|
private render(top: number) {
|
|
341
341
|
if (!this.elements) return;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
342
|
+
const { surface, content, backdrop } = this.elements;
|
|
343
|
+
const { sheetTop, pull } = this.split(top);
|
|
344
|
+
surface.style.transform = `translate3d(0, ${sheetTop}px, 0)`;
|
|
345
|
+
content.style.transform = pull > 0 ? `translate3d(0, ${pull}px, 0)` : '';
|
|
346
|
+
backdrop.style.opacity = String(this.backdropAlpha(sheetTop));
|
|
347
|
+
this.renderRefreshIndicator(pull);
|
|
345
348
|
}
|
|
346
349
|
|
|
347
350
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
351
|
+
* With a refresh indicator, a pull below the lowest detent keeps the sheet
|
|
352
|
+
* there and moves the content down inside it instead, uncovering the
|
|
353
|
+
* indicator at the top of the sheet.
|
|
350
354
|
*/
|
|
351
|
-
private
|
|
352
|
-
const
|
|
353
|
-
const visible =
|
|
355
|
+
private split(top: number): { sheetTop: number; pull: number } {
|
|
356
|
+
const pullsContent =
|
|
354
357
|
this.config.refreshing != null &&
|
|
358
|
+
this.config.pullToRefreshEnabled &&
|
|
359
|
+
!this.config.dismissOnSwipe &&
|
|
355
360
|
!this.entering &&
|
|
356
361
|
!this.isDismissing &&
|
|
357
362
|
!this.isOffscreen;
|
|
358
363
|
const lowest = Math.max(...this.detentTops);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
+
if (!pullsContent || !(top > lowest)) return { sheetTop: top, pull: 0 };
|
|
365
|
+
return { sheetTop: lowest, pull: top - lowest };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Centered in the gap a pull uncovers above the content, fading in as the
|
|
370
|
+
* pull nears the threshold.
|
|
371
|
+
*/
|
|
372
|
+
private renderRefreshIndicator(pull: number) {
|
|
373
|
+
const { refreshIndicator, refreshSpinner } = this.elements!;
|
|
374
|
+
const progress = Math.min(Math.max(pull / REFRESH_THRESHOLD, 0), 1);
|
|
375
|
+
const width = sheetWidth(this.viewport.width, this.config.isInline);
|
|
376
|
+
const x = (width - REFRESH_INDICATOR_SIZE) / 2;
|
|
377
|
+
const y = (pull - REFRESH_INDICATOR_SIZE) / 2;
|
|
364
378
|
refreshIndicator.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
365
379
|
refreshIndicator.style.opacity = String(progress);
|
|
366
380
|
if (progress > 0 && !this.refreshSpin) {
|
|
@@ -513,6 +527,9 @@ export class SheetPresenter extends ModalPresenter {
|
|
|
513
527
|
drag.rawTop += delta;
|
|
514
528
|
drag.sheetDrove = true;
|
|
515
529
|
}
|
|
530
|
+
if (!this.canDragBelowLowestDetent) {
|
|
531
|
+
drag.rawTop = Math.min(drag.rawTop, Math.max(...this.settledTops));
|
|
532
|
+
}
|
|
516
533
|
const top = this.constrained(drag.rawTop);
|
|
517
534
|
if (drag.canRefresh) {
|
|
518
535
|
drag.refreshArmed =
|
|
@@ -594,6 +611,15 @@ export class SheetPresenter extends ModalPresenter {
|
|
|
594
611
|
return ((last.y - first.y) / (last.time - first.time)) * 1000;
|
|
595
612
|
}
|
|
596
613
|
|
|
614
|
+
/**
|
|
615
|
+
* Whether a drag can take the sheet below its lowest detent: to dismiss it
|
|
616
|
+
* or to pull to refresh. Otherwise it stops there, like Android's
|
|
617
|
+
* `BottomSheetBehavior`.
|
|
618
|
+
*/
|
|
619
|
+
private get canDragBelowLowestDetent(): boolean {
|
|
620
|
+
return this.config.dismissOnSwipe || this.config.pullToRefreshEnabled;
|
|
621
|
+
}
|
|
622
|
+
|
|
597
623
|
/** Rubber-bands above the tallest detent, and below the lowest one when swiping can't dismiss. */
|
|
598
624
|
private constrained(top: number): number {
|
|
599
625
|
const tops = this.settledTops;
|