這次的side bar不一樣了,上一次我好像做的太簡單了,所以完讀率只有14%,我好難過啊(;´д`)ゞ,所以我這準備的內容有多一點。
這次我創了兩個檔案 SideBar.tsx 跟 SideBarItem.tsx 並放在SideBar的資料夾
const SideBarItem = ({ icon, handleClick, name, }: { icon: React.ReactNode, handleClick: Function, name: string}) => {
return (
<div className={`grid grid-cols-[80px-auto] bg-slate-900 rounded-lg`} onClick={() => { handleClick() }}>
<div className="col-start-1 flex justify-center items-center size-20">
{icon}
</div>
<p className={`col-start-2 hidden text-4xl`}>{name}</p>
</div>
)
}
export default SideBarItem
SideBarItem會有圖片以及展開後的文字,所以我用grid切出兩個區域,接著設定這些tag的顏色、大小
P.S. 展開後的文字等等再處理,所以我目前先讓他hidden
import { IoIosMenu, IoIosCalendar, IoIosHome, IoIosSettings, IoIosHelp } from "react-icons/io";
import { MdOutlineTask } from "react-icons/md";
import SideBarItem from "./SideBarItem";
const SideBar = () => {
return (
<div className={`bg-black text-white p-3`}>
<SideBarItem handleClick={() => { }} icon={<IoIosMenu size="40" />} name=" " />
<SideBarItem handleClick={() => { }} icon={<IoIosHome size="40" />} name='Home' />
<SideBarItem handleClick={() => { }} icon={<MdOutlineTask size="40" />} name='Task' />
<SideBarItem handleClick={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' />
<SideBarItem handleClick={() => { }} icon={<IoIosHelp size="70" />} name='Help' />
<SideBarItem handleClick={() => { }} icon={<IoIosSettings size="40" />} name='Settings' />
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>
)
}
export default SideBar
接著在SideBar裡,把所需要的Icon import進來,並將資料都丟進去SideBarItem裡,
最後加入分隔線
<div className={` h-[3px] bg-slate-200 rounded-sm`}></div>
完成後會長這樣
按鈕我分成三種
所以我讓最外層是grid,分出四格
<div className={`grid grid-rows-[auto_auto_1fr_auto] gap-5 bg-black text-white p-3`}>
<div className="row-start-1">
{/* 第一種 */}
<SideBarItem handleClick={() => { }} icon={<IoIosMenu size="40" />} name=" " />
</div>
<div className="row-start-4">
{/* 分隔線 */}
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>
<div className={`row-start-3 space-y-3`}>
{/* 第二種 */}
<SideBarItem handleClick={() => { }} icon={<IoIosHome size="40" />} name='Home' />
<SideBarItem handleClick={() => { }} icon={<MdOutlineTask size="40" />} name='Task' />
<SideBarItem handleClick={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' />
</div>
<div className="row-start-4 space-y-3">
{/* 第三種 */}
<SideBarItem handleClick={() => { }} icon={<IoIosHelp size="70" />} name='Help' />
<SideBarItem handleClick={() => { }} icon={<IoIosSettings size="40" />} name='Settings' />
</div>
</div>
為了讓滑鼠滑過去要有反應,我加了:hover跟cursor-pointer
在SideBarItem最外層的div上
hover:bg-slate-300 hover:text-black //顏色(背景、圖片)
hover:rounded-[25px] //邊框
cursor-pointer // 滑鼠鼠標
但這樣一瞬間變化太大,螢幕閃爍會害眼睛很不舒服,所以要在多加這一行
transition-all duration-200 ease-linear
這是整篇最麻煩的,首先先創一個變數isOpen以及func控制Side Bar 的開關。
const [isOpen, setisOpen] = useState<boolean>(false)
function toggleIsOpen(): void{
setisOpen(!isOpen);
}
將toggleIsOpen()丟進去handleClicked
<SideBarItem handleClick={() => { toggleIsOpen() }} icon={<IoIosMenu size="40" />} name=" " />
接著把IsOpen傳入SideBarItem的props
並加入判斷是裡控制寬度
transition-all duration-200 ease-linear //這個也順便丟進去
<p className={`col-start-2 flex items-center text-white text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>
歐對了,如果沒加overflow-hidden,SideBar會變這樣
但這展開時,menu也伸長了
為了避免這個情況
要給裝menu的div flex屬性之外,還要讓SideBarItem判斷是否需要<p>
SideBarItem.tsx
{name === '' ? <></> : <p className={`col-start-2 flex items-center text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>}
SideBarItem.tsx
const SideBarItem = ({ icon, handleClicked, name, isOpen}: { icon: React.ReactNode, handleClicked: Function, name: string, isOpen: boolean}) => {
return (
<div className={`grid grid-cols-[80px-auto] bg-slate-900 rounded-lg
hover:bg-slate-300 hover:text-black hover:rounded-[25px]
transition-all duration-200 ease-linear cursor-pointer`} onClick={() => { handleClicked() }}>
<div className="col-start-1 flex justify-center items-center size-20">
{icon}
</div>
{name === '' ? <></> : <p className={`col-start-2 flex items-center text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>}
</div>
)
}
export default SideBarItem
SideBar.tsx
import { IoIosMenu, IoIosCalendar, IoIosHome, IoIosSettings, IoIosHelp } from "react-icons/io";
import { MdOutlineTask } from "react-icons/md";
import SideBarItem from "./SideBarItem";
import { useState } from "react";
const SideBar = () => {
const [isOpen, setisOpen] = useState<boolean>(false)
function toggleIsOpen(): void {
setisOpen(!isOpen);
}
return (
<div className={`grid grid-rows-[auto_auto_1fr_auto] gap-5 bg-black text-white p-3`}>
<div className="row-start-1 flex">
{/* 第一種 */}
<SideBarItem handleClicked={() => { toggleIsOpen();}} icon={<IoIosMenu size="40" />} name="" isOpen={isOpen}/>
</div>
<div>
{/* 分隔線 */}
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>
<div className={`row-start-3 space-y-3`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen}/>
</div>
<div className="row-start-4 space-y-3">
{/* 第三種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHelp size="70" />} name='Help' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<IoIosSettings size="40" />} name='Settings' isOpen={isOpen}/>
</div>
</div>
)
}
export default SideBar
這個部份我是用position: absolute去做到的,所以我需要一個變數去儲存箭頭目的地的y座標
為此,我用了useState
const [arrowPosition, setArrowPosition] = useState<string>('[8px]')
接著
import箭頭到SideBar.tsx裡
import { FaCaretRight } from "react-icons/fa";
加進去div裡
<div className={`row-start-3 space-y-3 relative`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen}/>
<div className={`absolute top-${arrowPosition} left-[-20px] transition-all duration-200 ease-linear`}>
{/* 箭頭 */}
<FaCaretRight size="40"/>
</div>
</div>
P.S.怕會有人不知道,如果子項用了position: absolute且想以父項作為基準點的話,則父項也要有position: relative 或 absolute 或...
目前應該會做到這
function onNavItemClicked(position: string): void {
if(arrowPosition === position){
toggleIsOpen() //順便加的功能
return
}
setArrowPosition(position)
//打開某個頁面,還沒寫
}
把func加進去
<div className={`row-start-3 space-y-3 relative`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { onNavItemClicked('[8px]') }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { onNavItemClicked('[100px]') }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { onNavItemClicked('[192px]') }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen}/>
<div className={`absolute top-${arrowPosition} left-[-20px] transition-all duration-200 ease-linear`}>
{/* 箭頭 */}
<FaCaretRight size="40"/>
</div>
</div>
我知道top-[ooopx]這東西其實可以變成數學算式,好像廢話,畢竟我每個Item的大小以及padding都是固定的。但我好懶歐。可能以後多一些功能像是能自由更改icon大小,或是動態增減SideBarItem的時候,在更改好了。
我也知道我一開始的圖的menu是置中的,但後來的是在旁邊,這是因為,這個程式是我重寫的(我一開始寫的時候寫的很亂),結果我後來比較喜歡這種放邊邊的樣式,反正如果想要置中,就多打一個 justify-center在menu的div的flex旁邊
反正就先這樣了,希望你們會喜歡。
P.S. 喜歡的話拜託拜託按個愛心,我會很開心
const SideBarItem = ({ icon, handleClicked, name, isOpen}: { icon: React.ReactNode, handleClicked: Function, name: string, isOpen: boolean}) => {
return (
<div className={`grid grid-cols-[80px-auto] bg-slate-900 rounded-lg
hover:bg-slate-300 hover:text-black hover:rounded-[25px]
transition-all duration-200 ease-linear cursor-pointer`} onClick={() => { handleClicked() }}>
<div className="col-start-1 flex justify-center items-center size-20">
{icon}
</div>
{name === '' ? <></> : <p className={`col-start-2 flex items-center text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>}
</div>
)
}
export default SideBarItem
import { IoIosMenu, IoIosCalendar, IoIosHome, IoIosSettings, IoIosHelp } from "react-icons/io";
import { MdOutlineTask } from "react-icons/md";
import SideBarItem from "./SideBarItem";
import { useState } from "react";
import { FaCaretRight } from "react-icons/fa";
const SideBar = () => {
const [isOpen, setisOpen] = useState<boolean>(false)
function toggleIsOpen(): void {
setisOpen(!isOpen);
}
const [arrowPosition, setArrowPosition] = useState<string>('[8px]')
function onNavItemClicked(position: string): void {
if (arrowPosition === position) {
toggleIsOpen()
return
}
setArrowPosition(position)
}
return (
<div className={`grid grid-rows-[auto_auto_1fr_auto] gap-5 bg-black text-white p-3`}>
<div className="row-start-1 flex">
{/* 第一種 */}
<SideBarItem handleClicked={() => { toggleIsOpen(); }} icon={<IoIosMenu size="40" />} name="" isOpen={isOpen} />
</div>
<div>
{/* 分隔線 */}
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>
<div className={`row-start-3 space-y-3 relative`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { onNavItemClicked('[8px]') }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen} />
<SideBarItem handleClicked={() => { onNavItemClicked('[100px]') }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen} />
<SideBarItem handleClicked={() => { onNavItemClicked('[192px]') }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen} />
<div className={`absolute top-${arrowPosition} left-[-20px] transition-all duration-200 ease-linear`}>
{/* 箭頭 */}
<FaCaretRight size="40" />
</div>
</div>
<div className="row-start-4 space-y-3">
{/* 第三種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHelp size="70" />} name='Help' isOpen={isOpen} />
<SideBarItem handleClicked={() => { }} icon={<IoIosSettings size="40" />} name='Settings' isOpen={isOpen} />
</div>
</div>
)
}
export default SideBar