53 lines
1.2 KiB
TypeScript
53 lines
1.2 KiB
TypeScript
"use client";
|
||
/**
|
||
* 通用底部弹层(Bottom Sheet)
|
||
*
|
||
*
|
||
*
|
||
* 设计目标:
|
||
* - portal 渲染到 body
|
||
* - 固定底部、最大宽度跟随 `--app-max-width`、顶部圆角 `--radius-bottom-sheet`
|
||
* - ESC 关闭 / 点击 scrim 关闭 / 打开时锁定 body 滚动
|
||
* - 简单 slide-up 动画(与原 Dart 行为接近)
|
||
* - 调用方只关心内容布局
|
||
*/
|
||
import { type ReactNode } from "react";
|
||
|
||
import { ModalPortal } from "./modal-portal";
|
||
import styles from "./bottom-sheet.module.css";
|
||
|
||
export interface BottomSheetProps {
|
||
open: boolean;
|
||
onClose: () => void;
|
||
children: ReactNode;
|
||
/** 蒙层透明度(0-1)。默认 0.45。 */
|
||
scrimOpacity?: number;
|
||
/** 禁用点击外部 / ESC 关闭。 */
|
||
persistent?: boolean;
|
||
/** ARIA 标签。 */
|
||
ariaLabel?: string;
|
||
}
|
||
|
||
export function BottomSheet({
|
||
open,
|
||
onClose,
|
||
children,
|
||
scrimOpacity = 0.45,
|
||
persistent = false,
|
||
ariaLabel,
|
||
}: BottomSheetProps) {
|
||
return (
|
||
<ModalPortal
|
||
open={open}
|
||
onClose={onClose}
|
||
scrimClassName={styles.scrim}
|
||
panelClassName={styles.panel}
|
||
scrimOpacity={scrimOpacity}
|
||
persistent={persistent}
|
||
ariaLabel={ariaLabel}
|
||
>
|
||
{children}
|
||
</ModalPortal>
|
||
);
|
||
}
|