iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Modern Web

關於寫react 那二三事系列 第 27

Day27 Primereact的Tabview 以及Tabmenu

  • 分享至 

  • xImage
  •  

今天來上一下昨天未完成的邏輯部分

import React, { useState, useRef, useEffect } from "react";
import { TabMenu } from 'primereact/tabmenu';
import { MenuItem } from 'primereact/menuitem';
import { TabView, TabPanel } from 'primereact/tabview';

const TabCompnent: React.FC = () => {
  const [activeIndex, setActiveIndex] = useState<number>(0);
  const [currLeft, setCurrLeft] = useState<number>(0);
  const [currLeft1, setCurrLeft1] = useState<number>(0);
  const menuRef = useRef<HTMLDivElement>(null);
  const viewRef = useRef<HTMLDivElement>(null);
  const [menuPos, setMenuPos] = useState<number[]>([]);
  const [viewPos, setViewPos] = useState<number[]>([]);

  const items: MenuItem[] = [
    { label: 'Dashboard', icon: 'pi pi-home' },
    { label: 'Transactions', icon: 'pi pi-chart-line' },
    { label: 'Products', icon: 'pi pi-list' },
    { label: 'Messages', icon: 'pi pi-inbox' },
    { label: 'Dashboard1Dashboard1Dashboard1', icon: 'pi pi-home' },
    { label: 'Transactions1Transactions1Transactions1', icon: 'pi pi-chart-line' },
    { label: 'Products1', icon: 'pi pi-list' },
    { label: 'Messages1Messages1Messages1', icon: 'pi pi-inbox' }
  ];

  useEffect(() => {
    if (menuRef.current) {
      const tabItems: NodeListOf<HTMLLIElement> = menuRef.current.querySelectorAll('.p-tabmenuitem');
      let len = tabItems.length
      if (len > 0) {
        let tabList: number[] = [];
        let w = 0;
        tabList.push(0)
        for (let i = 0; i < len; i++) {
          w += tabItems[i].offsetWidth
          tabList.push(w)
        }
        setMenuPos(tabList)
      }

    }
    if (viewRef.current) {
      const tabItems: NodeListOf<HTMLLIElement> = viewRef.current.querySelectorAll('.p-unselectable-text');
      let len = tabItems.length
      if (len > 0) {
        let tabList: number[] = [];
        let w = 0;
        tabList.push(0)
        for (let i = 0; i < len; i++) {
          w += tabItems[i].offsetWidth
          tabList.push(w)
        }
        setViewPos(tabList)
      }
    }
  }, [])
  const handleMenuLeft = () => {
    if (!menuRef.current) return;
    let inx = menuPos.indexOf(currLeft)
    if (inx > 0) {
      setCurrLeft((menuPos[inx - 1]))
      menuRef.current?.querySelectorAll('.p-tabmenu')[0].scrollTo({
        left: menuPos[inx - 1],
        behavior: 'smooth'
      });
    }
  }
  const handleMenuRight = () => {
    if (!menuRef.current) return;
    let inx = menuPos.indexOf(currLeft)
    let totalW = menuPos[menuPos.length - 1]
    if ((totalW - menuRef.current.offsetWidth) < currLeft) return;
    setCurrLeft((menuPos[inx + 1]))
    menuRef.current?.querySelectorAll('.p-tabmenu')[0].scrollTo({
      left: menuPos[inx + 1],
      behavior: 'smooth'
    });
  }
  const handleViewLeft = () => {
    if (!viewRef.current) return;
    let inx = viewPos.indexOf(currLeft1)
    if (inx > 0) {
      setCurrLeft1(viewPos[inx - 1])
      viewRef.current?.querySelectorAll('.p-tabview-nav-content')[0].scrollTo({
        left: viewPos[inx - 1],
        behavior: 'smooth'
      });
    }
  }
  const handleViewRight = () => {
    if (!viewRef.current) return;
    let inx = viewPos.indexOf(currLeft1)
    let totalW = viewPos[viewPos.length - 1]
    if ((totalW - viewRef.current?.offsetWidth) < currLeft1) return;
    setCurrLeft1((viewPos[inx + 1]))
    viewRef.current?.querySelectorAll('.p-tabview-nav-content')[0].scrollTo({
      left: viewPos[inx + 1],
      behavior: 'smooth'
    });
  }
  return (
    <div>
      <div className="p-tabmenu-cus">
        <div className="p-leftbtn" onClick={handleMenuLeft} />
        <div ref={menuRef} style={{ overflow: 'hidden' }}><TabMenu model={items} activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)} /></div>
        <div className="p-rightbtn" onClick={handleMenuRight} />
      </div>
      <div className="p-tabview-cus">
        <div className="p-leftbtn" onClick={handleViewLeft} />
        <div ref={viewRef} style={{ overflow: 'hidden' }}>
          <TabView pt={{ panelContainer: { hidden: true } }}>
            {items.map((tab: MenuItem) => {
              return (
                <TabPanel key={`${tab.label}_`} leftIcon={`${tab.icon} mr-2`} header={tab.label} />
              );
            })}
          </TabView>
        </div>
        <div className="p-rightbtn" onClick={handleViewRight} />
      </div>
    </div>
  );
}
export default TabCompnent;

因為在點左點右時都要去找item寬度,避免每次都還要詢問一次,
所以在剛載入好時,將項目的left位置記憶好,

但因為RWD關係,所以只能在點擊當下判斷當前能顯示的寬度

至於其他的部分,我建議是不要直接取primereact class
而是用pt幫控制項目添加獨有className
主要添加各元件上管理方便

以tabmenu 來說 item的部分 可以在pt的menuitem添加className
而 scrollTo 則是 可以在 pt root 增加className控制

有遇過也很好玩的,當視窗resize時,就自動定位到當前點選的項目
可以用scrollIntoView 等等呈現

因為只是展示,所以就沒特別的做了

那麼,明天見


上一篇
Day26 Primereact的Tabview 以及Tabmenu
下一篇
Day28 Primereact 的後記
系列文
關於寫react 那二三事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言