Re從零開始的UI庫編寫生活-步驟管理組件Steps

鼓搗一下文字,講一講工做中遇到的趣事,今日來聊一聊步驟管理器。話說一日,產品經理帶着和善的眼神緩步踱來,對着前端的頁面這樣分析道,前端同窗啊,你看,咱們的頁面操做對用戶來說是零散的,用起來有好多不便,缺少一個有效的引導,應該怎麼去解決呢?哈哈,心想這問題不大。就說,我看不如這樣吧,咱們能夠把這些零散又頗有必要的操做集中起來,作成一個操做引導頁,這樣用戶就無需跳轉不一樣的頁面去完成那些零散的操做了,是對用戶體驗的一個很大提高。與此同時,朋友A說他也想要用這樣的方案去提高用戶體驗。ok,那麼一個便捷可配置,通用性良好的步驟管理組件應該怎樣去設計呢?css

開始設計

如下均已React爲例,但用Vue,Angular的實現大同小異。前端

靜下心來想想,要知足可靈活配置,高通用性,高可用性可並不容易,靈活配置和使用便捷是兩個對立的面,如何去平衡好是一件重要的事情。例如我想要靈活配置,就免不了要多寫代碼,這樣組件使用起來就會麻煩,參數容易忘記,要時不時看文檔。git

咱們先從保證通用性的角度出發,一步步去解決這個問題。github

組成

image

通常來講一個步驟管理組件由三個部分組成,分別是給與每一步步驟信息的標題部分,內容展現部分以及操控上/下一步的控制部分。sql

數據結構

根據組成來拆分,咱們能夠這樣去配置每個步驟。後端

// demo.jsx
<Steps stepset={[{
    title: '1',//標題
    content: <p>1</p>,//內容
    handleNext: () => { return true;}//控制true or false
}, {
    title: '2',
    content: <p>2</p>,
    handleNext: () => { return true;}
}, {
    title: '3',
    content: <p>3</p>,
    handleNext: () => { return true;}
}]} />
複製代碼

嗯,這很直觀。sass

狀態

image

每個步驟都有三種狀態,待選狀態,當前狀態,已選狀態。咱們能夠輕易地經過當前步驟currentStep去判斷每一步究竟屬於哪一種狀態。bash

// steps.jsx
...
handleTitleStyle(stepIndex, currentStep) {
    if (stepIndex > currentStep) {
        return 'step-sign-default';
    }
    if (stepIndex < currentStep) {
        return 'step-sign-ed';
    }
    return 'step-sign-active';
}
...
<div className={['orther-style', this.handleTitleStyle(index,currentStep)].join(' ')}>{item.title}</div>
...
複製代碼

佈局

image

佈局的要點在於步驟標題欄的自適應和通用性上。markdown

使用flex佈局令標題欄上的‘零件’整齊地自適應數據結構

// steps.scss
.steps-box{
    display: flex;
}
.steps-item{
    flex: 1;
}
...
複製代碼

使用相鄰兄弟選擇符巧妙地識別出首個步驟,令橫槓不在第一個步驟前顯示。

// steps.scss
...
.step-item+.step-item .sign-box {
    &:before {
        content: '';
        position: absolute;
        display: inline-block;
        border-style: solid;
        border-width: 1px;
        border-color: #cdcdcd;
        width: 100%;
        height: 0;
        left: -50%;
        top: 46%;
        transition: all .2s;
    }
}
...
複製代碼

內容展現

除當前步驟外,其餘步驟使用position:absolute;脫出文檔流,僅留下當前步驟所佔據的空間。

// steps.jsx
...
this.props.stepset.map((step, index) => {
    return (
        <div key={index} className="p-r">
            <div className="step-content" style={index == this.state.currentStep ? { position: 'relative', visibility: 'visible', zIndex: 1, left: 0 } : {}}>
                {
                    step.content
                }
            </div>
        </div>
    )
})
...
複製代碼

控制欄

image

沒錯,這裏就是靈活配置與方便使用的平衡點。若是爲了最大靈活配置考慮,控制欄的存在是沒有必要的,徹底能夠將上一步or下一步每個時刻的控制權交給開發者。但這樣未免要開發者寫不少控制代碼,因此筆者認爲這些控制代碼能夠交由組件去完成,咱們只要在合適的地方提供相關的API,開發者也能徹底控制步驟組件的每個時刻。這樣的折中方案即考慮到了靈活配置,又兼顧到了易用性。

一般在處理下一步的響應時,常常有異步的場景,例如點擊下一步時須要遠程校驗一下驗證碼。嗯,咱們能夠用async/await去構建一個支持異步的處理程序。

// steps.jsx
...
async handelNextStep() {
    const len = this.props.stepset.length;
    if (this.props.stepset[this.state.currentStep].handleNext) {
        // 支持異步操做 return Promise
        const AllowNext = await this.props.stepset[this.state.currentStep].handleNext(this.props.stepset[this.state.currentStep], this.state.currentStep);
        if (AllowNext) {
            if (this.state.currentStep + 1 < len) {
                this.setState({
                    currentStep: this.state.currentStep + 1
                });
            }
        }
    } else {
        if (this.state.currentStep + 1 < len) {
            this.setState({
                currentStep: this.state.currentStep + 1
            });
        }
    }
}

handelPreStep() {
    if (this.state.currentStep - 1 >= 0) {
        this.setState({
            currentStep: this.state.currentStep - 1
        }, () => {
            this.props.stepset[this.state.currentStep].handlePre && this.props.stepset[this.state.currentStep].handlePre(this.props.stepset[this.state.currentStep], this.state.currentStep);
        });
    }
}
...
複製代碼

朋友A這時又問了,那我想在content中去控制步驟怎麼辦呢?嗯,這不是問題,只要將相關的操做API暴露出來,經過props傳入便可。就像這樣:

// demo.jsx
<Steps stepset={[{
    title: '1',//標題
    content: (handelNextStep, handelPreStep) => {
        <p onClick={() => handelNextStep()}>hello</p>
    },
    isCustomCtrl: true//是否自定義控制欄
}, {
    title: '2',
    content: <p>2</p>,
    handleNext: () => { return new Promise((resolve) => { resolve(true); }) }//支持異步操做
}]} />
複製代碼

ok,一個步驟管理組件就成型了。

源碼

源碼已放到GitHub 組件&&樣式,能夠在SluckyUI中的‘步驟管理Steps’標籤下查看在線Demo。

這個步驟管理組件的故事到此就結束啦,產品經理與朋友A都很高興。這個步驟管理組件現已集成在SluckyUI For React中,SluckyUI的理念是打造一個組件庫種子,讓其餘開發者可以進行快速二次開發,減小沒必要要的造輪子,但當中的編寫任重而道遠,還有不少還沒有完善的地方,歡迎多多交流加入SluckyUI。

從零開始系列傳送門

相關文章
相關標籤/搜索