如何在 Material UI 中建立增強型傳輸列表?


在這篇文章中,我們將看到一個逐步指南,用於在 React MUI 中建立增強型傳輸列表。

傳輸列表是一種列表型別,允許使用者將一個或多個列表項移動到另一個列表。在這裡,如果第一個列表中有多個專案,並且使用者希望將某些專案傳輸到第二個列表,那麼我們使用傳輸列表元件。在 React MUI 中,沒有專門用於傳輸列表的元件;而是,我們自己建立它。

Material UI 中還有一個增強列表的概念,它只是基本傳輸列表的更高階或增強版本。我們可以在增強列表中包含各種內容,例如複選框、應用自定義顏色等。

建立增強型傳輸列表的步驟

以下是建立 Material UI 中增強型傳輸列表的步驟:

步驟 1:建立 React 應用程式

在 MUI 中建立傳輸列表的第一步是建立一個 React 應用程式。要建立一個新的 React 應用程式,請在您的終端中執行以下命令:

npx create react app formcontrolproject

專案建立完成後,透過執行以下命令導航到其目錄:

cd formcontrolproject

步驟 2:將 MUI 新增到 React

建立 React 應用程式後,是時候將 Material UI 安裝到 React 應用程式中了。要安裝 MUI,請執行以下命令:

npm install @mui/material @emotion/react @emotion/styled

步驟 3:定義專案

在建立傳輸列表之前,我們必須定義列表項,並在左右兩側設定一些值。以下是定義專案項的語法:

const [lftItems, setLftItems] = useState([1, 2, 3 ]);
const [rytItems, setRytItems] = useState([4, 5, 6]);
const [chosenLftItems, setChosenLftItems] = useState([]);
const [chosenRytItems, setChosenRytItems] = useState([]);

步驟 4:建立傳輸按鈕

要傳輸列表項,我們必須定義具有處理函式的傳輸按鈕。以下是如何在 React 中建立四個不同的按鈕:

const handlePush = () => {
   …
};

const handlePull = () => {
   …
};

const selectLftItem = (i) => {
   …
};

const selectRightItem = (i) => {
   …
};

步驟 5:在 Main 中呈現列表項

最後,一旦我們定義了所有處理列表傳輸的函式,我們現在必須使用 Stack 在 App.js 元件中呈現列表項。以下是呈現列表項的語法:

function App() {
   return (
      <Stack>
         <Stack item sx={{ gap: 2 }}>
            {lftItems.map((item, index) => (
               <ListItem key={index}>
                  …
               </ListItem>
            ))}
         </Stack>
         <Stack item>
            <Stack>
               <Button>
                  >
               </Button>
               <Button>
                  <
               </Button>
            </Stack>
         </Stack>
         <Stack sx={{ gap: 2 }}>
            {rytItems.map((item, index) => (
               <ListItem key={index}>
                  …
               </ListItem>
            ))}
         </Stack>
      </Stack>
   )
}

export default App;

就是這樣!現在我們已經成功地學習了在 MUI 中建立增強型傳輸列表的步驟。所以,讓我們看看一些不同方法的示例。

示例

在此示例中,我們建立了一個自定義的增強型傳輸列表,當用戶選擇任何要傳輸的專案時,複選框將被啟用。在這裡,使用者還可以一次選擇所有列表項以傳輸到其他列表。

import React from "react";
import { useState } from "react";
import { Stack } from "@mui/material";
import {List, ListItem, ListItemText, ListItemIcon} from '@mui/material';
import Checkbox from '@mui/material/Checkbox';
import Button from '@mui/material/Button';
import {Card, CardHeader} from '@mui/material/';

function not(a, b) {
   return a.filter((value) => b.indexOf(value) === -1);
}

function intersection(a, b) {
   return a.filter((value) => b.indexOf(value) !== -1);
}

function union(a, b) {
   return [...a, ...not(b, a)];
}

const App = () => {
   const [chk, setChk] = useState([]);
   const [lftItems, setLftItems] = useState(["Item 11",
      "Item 12",
      "Item 13",
      "Item 14",
      "Item 15"]);
   const [rightItems, setRightItems] = useState(["Item 21",
      "Item 22",
      "Item 23",
      "Item 24",
      "Item 25"]);

   const chkLeftChecked = intersection(chk, lftItems);
   const chkRightChecked = intersection(chk, rightItems);

   const handleSingleToggle = (val) => () => {
      const currentIdx = chk.indexOf(val);
      const latestChecked = [...chk];

      if (currentIdx === -1) {
         latestChecked.push(val);
      } else {
         latestChecked.splice(currentIdx, 1);
      }

      setChk(latestChecked);
   };

   const chkNo = (items) => intersection(chk, items).length;

   const handleMultipleToggle = (i) => () => {
      if (chkNo(i) === i.length) {
         setChk(not(chk, i));
      } else {
         setChk(union(chk, i));
      }
   };

   const pushRight = () => {
      setRightItems(rightItems.concat(chkLeftChecked));
      setLftItems(not(lftItems, chkLeftChecked));
      setChk(not(chk, chkLeftChecked));
   };

   const pushLeft = () => {
      setLftItems(lftItems.concat(chkRightChecked));
      setRightItems(not(rightItems, chkRightChecked));
      setChk(not(chk, chkRightChecked));
   };

   const ListComponent = (listItemName, ListItems) => (
      <Card sx={{ p: 3 }}>
         <CardHeader
            sx={{ p: 2 }}
            avatar={
               <Checkbox
                  onClick={handleMultipleToggle(ListItems)}
                  checked={chkNo(ListItems) === ListItems.length && ListItems.length !== 0}
                  indeterminate={
                     chkNo(ListItems) !== ListItems.length && chkNo(ListItems) !== 0
                  }
                  disabled={ListItems.length === 0}
               />
            }
            title={listItemName}
            subheader={`${chkNo(ListItems)}/${ListItems.length} selected`}
         />
         <hr />
         <List sx={{overflow: 'auto',}}>
            {ListItems.map((value) => {
               const labelId = `transfer-list-all-item-${value}-label`;
               return (
                  <ListItem
                     key={value}
                     onClick={handleSingleToggle(value)}
                     >
                     <ListItemIcon>
                        <Checkbox
                           checked={chk.indexOf(value) !== -1}
                           tabIndex={-1}
                           disableRipple
                        />
                     </ListItemIcon>
                     <ListItemText id={labelId} primary={value} />
                  </ListItem>
               );
            })}
         </List>
      </Card>
   );

   return (
      <div style={{
            padding: 40,
            display: 'flex',
            flexDirection: 'column',
            gap: 20,
            backgroundColor: 'lightcyan'
         }}>
         <Stack direction="row" container spacing={5}>
            <Stack item>{ListComponent('Select from below', lftItems)}</Stack>
            <Stack item>
               <Stack container direction="column" sx={{ gap: 5 }} alignItems="center">
                  <Button
                     variant="contained"
                     color="info"
                     onClick={pushRight}
                     disabled={chkLeftChecked.length === 0}
                     >
                     >
                  </Button>
                  <Button
                     variant="contained"
                     color="info"
                     onClick={pushLeft}
                     disabled={chkRightChecked.length === 0}
                     >
                     <
                  </Button>
               </Stack>
            </Stack>
            <Stack item>{ListComponent('Selected', rightItems)}</Stack>
         </Stack>
      </div>
   );
};

export default App;

輸出

示例

在此示例中,我們建立了一個自定義的增強型傳輸列表,當用戶選擇任何要傳輸的專案時,複選框將被啟用。在這裡,列表項使用不同的顏色進行自定義。

import React, { useState } from "react";
import { Stack, Checkbox, ListItem, ListItemText, Button } from "@mui/material";

const App = () => {
   const [lftItems, setLftItems] = useState([1, 2, 3]);
   const [rytItems, setRytItems] = useState([4, 5, 6]);
   const [chosenLftItems, setChosenLftItems] = useState([]);
   const [chosenRytItems, setChosenRytItems] = useState([]);

   const handlePush = () => {
      setRytItems((before) => [...before, ...chosenLftItems]);
      setLftItems((prevItems) =>
         prevItems.filter((_, index) => !chosenLftItems.includes(index))
      );
      setChosenLftItems([]);
   };

   const handlePull = () => {
      setLftItems((before) => [...before, ...chosenRytItems]);
      setRytItems((prevItems) =>
         prevItems.filter((_, index) => !chosenRytItems.includes(index))
      );
      setChosenRytItems([]);
   };

   const selectLftItem = (i) => {
      if (chosenLftItems.includes(i)) {
         setChosenLftItems((beforeSelected) =>
            beforeSelected.filter((item) => item !== i)
         );
      } else {
         setChosenLftItems((beforeSelected) => [...beforeSelected, i]);
      }
   };

   const selectRightItem = (i) => {
      if (chosenRytItems.includes(i)) {
         setChosenRytItems((beforeSelected) =>
            beforeSelected.filter((item) => item !== i)
         );
      } else {
         setChosenRytItems((beforeSelected) => [...beforeSelected, i]);
      }
   };

   return (
      <div
         style={{
            padding: 40,
            display: "flex",
            flexDirection: "column",
            gap: 20,
            backgroundColor: "lightcyan"
         }}>
         <Stack direction="row" container spacing={5}>
            <Stack sx={{ gap: 2 }}>
               {lftItems.map((item, index) => (
                  <ListItem
                     key={index}
                     onClick={() => selectLftItem(index)}
                     sx={{
                        backgroundColor: "lightblue",
                        borderRadius: 1,
                        cursor: "pointer"
                     }}>
                     <Checkbox
                        checked={chosenLftItems.includes(index)}
                        color="primary"
                     />
                     <ListItemText primary={item} />
                  </ListItem>
               ))}
            </Stack>
            <Stack item>
               <Stack
                  container
                  justifyContent="center"
                  direction="column"
                  sx={{ gap: 3 }}
                  alignItems="center"
                  >
                  <Button
                     variant="contained"
                     color="info"
                     onClick={handlePush}
                     disabled={chosenLftItems.length === 0}
                     >
                     >
                  </Button>
                  <Button
                     variant="contained"
                     color="info"
                     onClick={handlePull}
                     disabled={chosenRytItems.length === 0}
                     >
                     <
                  </Button>
               </Stack>
            </Stack>
            <Stack sx={{ gap: 2 }}>
               {rytItems.map((item, index) => (
                  <ListItem
                     key={index}
                     onClick={() => selectRightItem(index)}
                     sx={{
                        backgroundColor: "lightblue",
                        borderRadius: 1,
                        cursor: "pointer"
                     }}
                     >
                     <Checkbox
                        checked={chosenRytItems.includes(index)}
                        color="primary"
                     />
                     <ListItemText primary={item} />
                  </ListItem>
               ))}
            </Stack>
         </Stack>
      </div>
   );
};

export default App;

輸出

結論

本文討論了在 React MUI 中建立增強型傳輸列表的完整細節。在本文中,我們學習了建立增強型傳輸列表的完整步驟,以及使用不同方法的不同示例。

更新於: 2023年11月1日

511 次瀏覽

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告