Modal組件
長話不多說,接下來讓我們來動手實現一個react Modal組件。
我們先來看一下實際效果
Modal的布局
首先,讓我們先思考下一個Modal組件的布局是怎麼樣的。
我們先拿一個基本的Modal範例來分析下。
如所示,一個Modal組件可以分為mask、header、body和footer四部分,mask就不用說了,header主要是顯示title和關閉按鈕,body則是使用者自己傳的內容,footer主要是按鈕控制項。
Modal組件的參數(props)
我們確定了Modal組件的布局之後,我們來思考一下Modal組件可支援傳遞的參數。
作為一個Modal組件,總要有標題(title)吧?要有使用者自訂傳入的內容(children),還有一個確定按鈕文案(okText)和一個取消按鈕文案(cancelText)吧,並且允許使用者傳入點擊確定按鈕的回呼函數(onOk)和點擊取消按鈕的回呼函數(onCancel)。也需要有一個控制Modal是否顯示的標誌吧(visible)。所以,大體上有以下7個變數。
Modal的樣式
首先,根據Modal組件的布局和參數,我們可以確定react Modal的render函數如下:
我們都知道,Modal會覆蓋在其他元素上面,並且主要分為兩部分,一部分為mask陰影部分,一部分為主體內容,而且主體部分會覆蓋在陰影部分上面。讓我們一步步來實現這個效果。
實現mask效果
.modal-mask { // 讓mask鋪滿整屏 position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: black; opacity: 0.6; // 讓mask覆蓋在其他元素上面 z-index: 1000;}
實現主體內容的樣式,讓其覆蓋在其他元素(包括mask)上面,每一部分的作用可以看注釋
.modal-container { // 讓Modal的主體內容全域置中,通過position: fix以及top和left的50%讓主體內容的左上方置中,再通過transform:translate(-50%, -50%)來讓主體內容正確置中。 position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; min-width: 500px; border-radius: 4px; // 設定主體內容的z-index高於mask的,從而可以覆蓋mask z-index: 1001;}
接下來是body、footer和header樣式的實現,這個就直接貼代碼了。
.modal-title { padding: 30px; color: black; font-size: 20px; border-bottom: 1px solid #e8e8e8;}.modal-body { padding: 30px; font-size: 14px; border-bottom: 1px solid #e8e8e8;}.modal-footer { text-align: center; padding: 30px; display: flex;}.modal-footer .btn { flex: 1; height: 32px; text-align: center;}.modal-footer .modal-cancel-btn { background: white; margin-right: 30px; border-color: #d9d9d9; border-radius: 4px;}.modal-footer .modal-confirm-btn { background: #1890ff; color: white; }
Modal的互動邏輯實現
實際上Modal的互動是很簡單的,一般的調用方式如下:
由外部傳遞自訂的body內容以及一些自訂的屬性(比如title,點擊按鈕的回調還有Modal的標題)
我們先定義Modal組件裡的props
設定一些預設的props,當使用者未傳入參數的時候,則使用預設的props
實現render函數,根據使用者傳入的參數以及預設參數來渲染Modal節點,如果使用者傳入的visible屬性為false(Modal不可見),則返回null,否則,返回Modal節點。
這樣,一個簡單的react Modal組件就完成了,上面的代碼可以在github.com/chenjigeng/empty 查看,並且可以直接看到一個demo例子。
如下:
最後再貼一下完整的Modal組件代碼
// Modal.tsximport * as React from 'react';import './Modal.css';interface IModalProps { children: React.ReactChild | React.ReactChildren | React.ReactElement<any>[], title?: React.ReactChild, visible: boolean, onOk?: () => void, onCancel?: () => void, okText?: string, cancelText?: string,} export default class Modal extends React.Component<IModalProps> { public static defaultProps = { cancelText: '取消', okText: '確定', visible: false, } public render() { const { title, visible, okText, cancelText, children, onOk, onCancel } = this.props; if (!visible) { return null; }; return ( <div> <div className="modal-mask" onClick={onCancel}/> <div className="modal-container"> <div className="modal-header"> <div className="modal-title">{title}</div> </div> <div className="modal-body"> {children} </div> <div className="modal-footer"> <button className="modal-cancel-btn btn" onClick={onCancel}>{cancelText}</button> <button className="modal-confirm-btn btn" onClick={onOk}>{okText}</button> </div> </div> </div> ) }}
// Moda.css.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: black; opacity: 0.6; z-index: 1000;}.modal-container { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; min-width: 500px; border-radius: 4px; z-index: 1001;}.modal-title { padding: 30px; color: black; font-size: 20px; border-bottom: 1px solid #e8e8e8;}.modal-body { padding: 30px; font-size: 14px; border-bottom: 1px solid #e8e8e8;}.modal-footer { text-align: center; padding: 30px; display: flex;}.modal-footer .btn { flex: 1; height: 32px; text-align: center;}.modal-footer .modal-cancel-btn { background: white; margin-right: 30px; border-color: #d9d9d9; border-radius: 4px;}.modal-footer .modal-confirm-btn { background: #1890ff; color: white; }