feat: add elevation prop for Android and Web (#355)

* feat(android): add elevation prop

* feat(web): add elevation prop support
This commit is contained in:
Jovanni Lo 2025-12-26 00:52:40 +08:00 committed by GitHub
parent dee02e15ea
commit 917775ee07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 69 additions and 3 deletions

View File

@ -222,6 +222,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
viewController.grabberOptions = options
}
fun setSheetElevation(elevation: Float) {
viewController.sheetElevation = elevation
}
fun setDetents(newDetents: MutableList<Double>) {
viewController.detents = newDetents
}

View File

@ -178,6 +178,12 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
if (isPresented) sheetView?.setupBackground()
}
override var sheetElevation: Float = -1f
set(value) {
field = value
if (isPresented) sheetView?.setupElevation()
}
var dismissible: Boolean = true
set(value) {
field = value
@ -647,6 +653,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
setupDimmedBackground(currentDetentIndex)
setupKeyboardObserver()
sheet.setupBackground()
sheet.setupElevation()
sheet.setupGrabber()
if (shouldAnimatePresent) {

View File

@ -198,6 +198,11 @@ class TrueSheetViewManager :
// iOS-specific prop - no-op on Android
}
@ReactProp(name = "elevation", defaultDouble = -1.0)
override fun setElevation(view: TrueSheetView, elevation: Double) {
view.setSheetElevation(elevation.toFloat())
}
companion object {
const val REACT_CLASS = "TrueSheetView"
const val TAG_NAME = "TrueSheet"

View File

@ -18,6 +18,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
interface TrueSheetBottomSheetViewDelegate {
val isTopmostSheet: Boolean
val sheetCornerRadius: Float
val sheetElevation: Float
val sheetBackgroundColor: Int?
val grabber: Boolean
val grabberOptions: GrabberOptions?
@ -37,6 +38,7 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
private const val GRABBER_TAG = "TrueSheetGrabber"
private const val DEFAULT_CORNER_RADIUS = 16f // dp
private const val DEFAULT_MAX_WIDTH = 640 // dp
private const val DEFAULT_ELEVATION = 4f // dp
}
// =============================================================================
@ -138,6 +140,12 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
}
}
fun setupElevation() {
val value = delegate?.sheetElevation ?: DEFAULT_ELEVATION
val effectiveElevation = if (value < 0) DEFAULT_ELEVATION else value
elevation = effectiveElevation.dpToPx()
}
// =============================================================================
// MARK: - Grabber
// =============================================================================

View File

@ -66,9 +66,17 @@ The sheet corner radius.
| `number` | _system default_ | ✅ | ✅ | ✅ |
:::info
When not provided, iOS uses the device's native corner radius automatically, while Android defaults to `28` (following Material Design 3 guidelines).
When not provided, iOS uses the device's native corner radius automatically, while Android defaults to `16` (following Material Design 3 guidelines).
:::
## `elevation`
The elevation (shadow depth) of the sheet.
| Type | Default | 🍎 | 🤖 | 🌐 |
| - | - | - | - | - |
| `number` | `4` | | ✅ | ✅ |
## `maxHeight`
Overrides `"large"` or `"100%"` height.

View File

@ -399,6 +399,15 @@ export interface TrueSheetProps extends ViewProps {
*/
insetAdjustment?: InsetAdjustment;
/**
* The elevation (shadow depth) of the sheet.
*
* @platform android
* @platform web
* @default 4
*/
elevation?: number;
/**
* A component that is fixed at the top of the Sheet content.
* Useful for search bars, titles, or other header content.

View File

@ -48,11 +48,30 @@ import type {
} from './TrueSheet.types';
const DEFAULT_CORNER_RADIUS = 16;
const DEFAULT_ELEVATION = 4;
const DEFAULT_GRABBER_COLOR = 'rgba(0, 0, 0, 0.3)';
const DEFAULT_GRABBER_WIDTH = 32;
const DEFAULT_GRABBER_HEIGHT = 4;
/**
* Converts elevation to CSS box-shadow based on Material Design 3 elevation system.
* Uses a combination of ambient and key shadows for realistic depth.
*/
const getElevationShadow = (elevation: number): string => {
if (elevation <= 0) return 'none';
const ambientY = elevation * 0.5;
const ambientBlur = elevation * 1.5;
const ambientOpacity = 0.08 + elevation * 0.01;
const keyY = elevation;
const keyBlur = elevation * 2;
const keyOpacity = 0.12 + elevation * 0.02;
return `0px ${ambientY}px ${ambientBlur}px rgba(0, 0, 0, ${ambientOpacity}), 0px ${keyY}px ${keyBlur}px rgba(0, 0, 0, ${keyOpacity})`;
};
const renderSlot = (slot: TrueSheetProps['header'] | TrueSheetProps['footer']) => {
if (!slot) return null;
if (isValidElement(slot)) return slot;
@ -72,6 +91,7 @@ export const TrueSheet = forwardRef<TrueSheetRef, TrueSheetProps>((props, ref) =
initialDetentIndex = -1,
backgroundColor = '#ffffff',
cornerRadius = DEFAULT_CORNER_RADIUS,
elevation = DEFAULT_ELEVATION,
grabber = true,
grabberOptions,
maxHeight,
@ -425,7 +445,12 @@ export const TrueSheet = forwardRef<TrueSheetRef, TrueSheetProps>((props, ref) =
const sharedProps = {
style: [
styles.root,
{ backgroundColor, borderTopLeftRadius: cornerRadius, borderTopRightRadius: cornerRadius },
{
backgroundColor,
borderTopLeftRadius: cornerRadius,
borderTopRightRadius: cornerRadius,
boxShadow: getElevationShadow(elevation),
},
],
index: snapIndex,
enablePanDownToClose: dismissible,
@ -469,7 +494,6 @@ export const TrueSheet = forwardRef<TrueSheetRef, TrueSheetProps>((props, ref) =
const styles = StyleSheet.create({
root: {
overflow: 'hidden',
boxShadow: '0px -2px 16px 0px rgba(9, 10, 9, 0.08)',
},
handle: {
position: 'absolute',

View File

@ -41,6 +41,7 @@ export interface NativeProps extends ViewProps {
// Number properties - use 0 as default to avoid nil insertion
maxHeight?: WithDefault<Double, 0>;
cornerRadius?: WithDefault<Double, -1>;
elevation?: WithDefault<Double, -1>;
// Color properties
backgroundColor?: ColorValue;