iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0
自我挑戰組

Material UI in React系列 第 15

Material UI in React [ Day15 ] Navigation Stepper 步驟卡

Stepper

Stepper 通過編號的步驟傳達進度,它提供了類似嚮導的工作流程。
他除了有前面提到的 Tabs 的屬性外還有提供紀錄進度的功能,可以通過將當前步進索引(從零開始)作為 activeStep 屬性傳遞來控制步進器,Stepper方向是使用 orientation 屬性設置的,所以如果要調整成垂直的狀態只要設置成 orientation="vertical",alternativeLabel 屬性則可以讓標籤的位置改再 icon 之下。

// 先設置step title
function getSteps() {
  return ['基本資料', '選擇商品型號', '配送資訊'];
}
// 再設置step content,可以用其他組件來替換
function getStepContent(stepIndex) {
  switch (stepIndex) {
    case 0:
      return '基本資料頁面...';
    case 1:
      return '選擇商品型號頁面...';
    case 2:
      return '配送資訊...';
    default:
      return 'step 未設定';
  }
}
// in export function
const [activeStep, setActiveStep] = React.useState(0);
const steps = getSteps();

const handleNext = () => {
  setActiveStep((prevActiveStep) => prevActiveStep + 1);
};

const handleBack = () => {
  setActiveStep((prevActiveStep) => prevActiveStep - 1);
};

const handleReset = () => {
  setActiveStep(0);
};
/*in return*/
<div className={classes.root}>
  <Stepper activeStep={activeStep} alternativeLabel>
    {steps.map((label) => (
      <Step key={label}>
        <StepLabel>{label}</StepLabel>
      </Step>
    ))}
  </Stepper>
  <div>
    {activeStep === steps.length ? (
      <div>
        <Typography className={classes.instructions}>填寫完畢</Typography>
        <Button onClick={handleReset}>重置</Button>
      </div>
    ) : (
      <div>
        <Typography className={classes.instructions}>{getStepContent(activeStep)}</Typography>
        <div>
          <Button
            disabled={activeStep === 0}
            onClick={handleBack}
            className={classes.backButton}
          >
            上一步
          </Button>
          <Button variant="contained" color="primary" onClick={handleNext}>
            {activeStep === steps.length - 1 ? '完成' : '下一步'}
          </Button>
        </div>
      </div>
    )}
  </div>
</div>

官網文件上有提供Skip的範例做法:

// ...前面的段落一樣
// in export function
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [skipped, setSkipped] = React.useState(new Set());
const isStepOptional = (step) => {
  return step === 1;
};

const isStepSkipped = (step) => {
  return skipped.has(step);
};

const handleNext = () => {
  let newSkipped = skipped;
  if (isStepSkipped(activeStep)) {
    newSkipped = new Set(newSkipped.values());
    newSkipped.delete(activeStep);
  }
  
  setActiveStep((prevActiveStep) => prevActiveStep + 1);
  setSkipped(newSkipped);
};

const handleBack = () => {
  setActiveStep((prevActiveStep) => prevActiveStep - 1);
};

const handleSkip = () => {
  if (!isStepOptional(activeStep)) {
    // 需要防範此情形,一般的使用情境下不太容易發生
    throw new Error("You can't skip a step that isn't optional.");
  }

  setActiveStep((prevActiveStep) => prevActiveStep + 1);
  setSkipped((prevSkipped) => {
    const newSkipped = new Set(prevSkipped.values());
    newSkipped.add(activeStep);
    return newSkipped;
  });
};

const handleReset = () => {
  setActiveStep(0);
};
<div className={classes.root}>
  <Stepper activeStep={activeStep}>
    {steps.map((label, index) => {
      const stepProps = {};
      const labelProps = {};
      if (isStepOptional(index)) {
        labelProps.optional =
        <Typography variant="caption">
            Optional
        </Typography>;
      }
      if (isStepSkipped(index)) {
        stepProps.completed = false;
      }
      return (
        <Step key={label} {...stepProps}>
          <StepLabel {...labelProps}>{label}</StepLabel>
        </Step>
      );
    })}
  </Stepper>
  <div>
    {activeStep === steps.length ? (
      <div>
        <Typography className={classes.instructions}>
          All steps completed - you&apos;re finished
        </Typography>
        <Button onClick={handleReset} className={classes.button}>
          Reset
        </Button>
      </div>
    ) : (
      <div>
        <Typography className={classes.instructions}>
          {getStepContent(activeStep)}
        </Typography>
        <div>
          <Button
            disabled={activeStep === 0}
            onClick={handleBack}
            className={classes.button}
          >
            Back
          </Button>
          {isStepOptional(activeStep) && (
            <Button
              variant="contained"
              color="primary"
              onClick={handleSkip}
              className={classes.button}
            >
              Skip
            </Button>
          )}

          <Button
            variant="contained"
            color="primary"
            onClick={handleNext}
            className={classes.button}
          >
            {activeStep === steps.length - 1 ? 'Finish' : 'Next'}
          </Button>
        </div>
      </div>
    )}
  </div>
</div>

可以依照實際的情況去修改邏輯,這個作法不是固定的。

Non-linear

設定 nonLinear 屬性,可以讓進程的線不亮。

<Stepper activeStep={activeStep} nonLinear alternativeLabel>
  {steps.map((label) => (
    <Step key={label}>
      <StepLabel>{label}</StepLabel>
    </Step>
  ))}
</Stepper>

另外還有類似 Carousel 的範例在官方的文件上,這裡我就不再贅述他的用法了,因為我相信通常在處理這種 lightbox 的東西會去用類似 splidejs 這種專門的套件,可能會比較方便一些。
那麼今天的內容就到這裡了,明天會講解 Menu 組件的用法。


上一篇
Material UI in React [ Day14 ] Navigation Tabs 選項卡
下一篇
Material UI in React [ Day 16 ] Navigation Menu (下拉框)
系列文
Material UI in React30

尚未有邦友留言

立即登入留言