如何在FluentUI Modal中实现固定头部/底部及内容变更时尺寸不变
实现方案
针对你提出的两个需求,只需要对原有代码做以下修改即可实现:
核心修改逻辑
- 头部、底部固定+中间内容滚动:通过flex布局实现,将模态框容器设为纵向flex布局,头部和底部设置为不可伸缩,中间内容区域设置为自动占满剩余空间,溢出时滚动。
- 初始适配视窗+后续尺寸固定:首次打开模态框时记录容器的初始渲染高度,之后将容器高度固定为该值,关闭模态框时重置记录值,下次打开重新适配。
完整修改后代码
JS代码
const { useBoolean } = window.FluentUIReactHooks; const { getTheme, mergeStyleSets, FontWeights, ContextualMenu, Toggle, Modal, IDragOptions, IIconProps, Stack, IStackProps, DefaultButton, IconButton, IButtonStyles, ThemeProvider, initializeIcons, Separator, PrimaryButton } = window.FluentUIReact; // Initialize icons in case this example uses them initializeIcons(); const ModalBasicExample: React.FunctionComponent = () => { const [isModalOpen, { setTrue: showModal, setFalse: hideModal }] = useBoolean(false); const [pageNo, setPageNo] = React.useState(0); const changePage = React.useCallback(() => setPageNo(oldVal => ~oldVal),[setPageNo]) // 新增:模态框容器引用、初始高度记录 const modalRef = React.useRef(null); const [modalHeight, setModalHeight] = React.useState(null); React.useEffect(() => { if (isModalOpen && modalRef.current && !modalHeight) { // 首次打开记录实际渲染高度 setModalHeight(modalRef.current.offsetHeight); } if (!isModalOpen) { // 关闭时重置,下次打开重新适配 setModalHeight(null); } }, [isModalOpen, modalHeight]); const titleId = "titleId"; return ( <div> <DefaultButton onClick={showModal} text="Open Modal" /> <Modal titleAriaId={titleId} isOpen={isModalOpen} onDismiss={hideModal} isBlocking={false} containerClassName={contentStyles.container} style={modalHeight ? {height: `${modalHeight}px`} : {}} topOffsetFixed={true} componentRef={modalRef} > <div className={contentStyles.header}> <span id={titleId}>Lorem Ipsum</span> <IconButton styles={iconButtonStyles} iconProps={cancelIcon} ariaLabel="Close popup modal" onClick={hideModal} /> </div> <div className={contentStyles.body}> {pageNo===0 ? <div dangerouslySetInnerHTML={{ __html:lorem}}></div> : <p>page {pageNo}</p>} </div> <footer className={contentStyles.footer}> <Separator/> <PrimaryButton text="switch" onClick={changePage}/> </footer> </Modal> </div> ); }; const cancelIcon: IIconProps = { iconName: 'Cancel' }; const theme = getTheme(); const contentStyles = mergeStyleSets({ container: { display: 'flex', flexFlow: 'column nowrap', alignItems: 'stretch', // 新增:最大高度不超过视窗的90%,固定宽度避免水平缩放 maxHeight: '90vh', width: '600px', }, header: [ theme.fonts.xLargePlus, { // 修改:头部不参与伸缩 flex: '0 0 auto', borderTop: `4px solid ${theme.palette.themePrimary}`, color: theme.palette.neutralPrimary, display: 'flex', alignItems: 'center', fontWeight: FontWeights.semibold, padding: '12px 12px 14px 24px', }, ], body: { // 修改:内容区域占满剩余空间,溢出自动滚动 flex: '1 1 auto', padding: '0 24px 24px 24px', overflowY: 'auto', selectors: { p: { margin: '14px 0' }, 'p:first-child': { marginTop: 0 }, 'p:last-child': { marginBottom: 0 }, }, }, footer: { // 修改:底部不参与伸缩,加内边距 flex: '0 0 auto', padding: '12px 24px', } }); const iconButtonStyles: Partial<IButtonStyles> = { root: { color: theme.palette.neutralPrimary, marginLeft: 'auto', marginTop: '4px', marginRight: '2px', }, rootHovered: { color: theme.palette.neutralDark, }, }; const lorem = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc suscipit, est eget porta tempor, ante justo finibus odio, sit amet rhoncus neque velit nec leo. Morbi vitae ex a urna auctor sagittis dictum at ante. Etiam vel commodo turpis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dapibus malesuada luctus. Vivamus nec convallis dolor, nec condimentum ligula. Aenean blandit mi et justo rutrum, sed feugiat erat gravida. Donec sit amet eleifend mauris. Duis eget leo vel elit viverra ullamcorper. Quisque eu purus nec diam convallis suscipit.</p>".repeat(7); const ModalBasicExampleWrapper = () => <ThemeProvider><ModalBasicExample /></ThemeProvider>; ReactDOM.render(<ModalBasicExampleWrapper />, document.getElementById('content'))
HTML代码
<script src="https://unpkg.com/@fluentui/react@8/dist/fluentui-react.js"></script> <script src="https://unpkg.com/@fluentui/react-hooks@8/dist/react-hooks.js"></script> <div id="content"></div>
内容的提问来源于stack exchange,提问作者Brent
相关产品推荐
相关产品推荐

