手写tabs 组件

 是说,会遇到一个问题,默认值play back很差,而且因为有动画,切换的时候给人体验不好

但是我仍然觉得我写的很好,所以记录这里

 

'use client';
import { cn } from '@/lib';
import React, { memo, ReactNode, useEffect, useState } from 'react';
import { FC } from 'react';

interface contentType {
  currentTabIndex: number;
  setCurrentTabIndex: (index: number) => void;
  tabsLength: number;
}
interface MethodClassProps {
  defaultTab: number;
  children: React.ReactNode;
  tabsLength: number;
}

const TabsContext = React.createContext<contentType | null>(null);

const useTabsContext = () => {
  const context = React.useContext(TabsContext);
  if (!context) {
    throw new Error('useTabsContext must be used within a TabsProvider');
  }
  return context;
};

// 必须保证TabsContent和triggerList的长度一致
export const Tabs: FC<MethodClassProps> = ({ ...props }) => {
  const { children, defaultTab, tabsLength } = props;
  const [currentTabIndex, setCurrentTabIndex] = useState<number>(defaultTab);

  return (
    <>
      <TabsContext.Provider
        value={{
          currentTabIndex: currentTabIndex,
          setCurrentTabIndex: setCurrentTabIndex,
          tabsLength: tabsLength,
        }}
      >
        {children}
      </TabsContext.Provider>
    </>
  );
};

export const TabsTrigger = ({
  extraContent,
  children,
  theme = 'Purple',
  onCurrentTabChange,
}: {
  extraContent?: ReactNode;
  children: ReactNode[];
  theme?: 'Orange' | 'Purple';
  onCurrentTabChange?: (tab: number) => void;
}) => {
  const { currentTabIndex, setCurrentTabIndex } = useTabsContext();
  const validChildren = React.Children.toArray(children).filter(
    (child) => React.isValidElement(child) && child !== null,
  );
  return (
    <section className=" relative lls-flex-row bg-white border-b shadow-header text-xs px-5 w-full">
      {validChildren.map((trigger, index) => (
        <div
          key={index + 'triggerlist'}
          className={cn(
            'lls-flex-row gap-3 py-2 pl-3 pr-7 border-b-3 justify-center items-center transition-all duration-500',
            {
              'border-b-white': currentTabIndex !== index + 1,
              'border-b-Purple-1 ':
                currentTabIndex === index + 1 && theme === 'Purple',
              'border-b-Orange-1':
                currentTabIndex === index + 1 && theme === 'Orange',
            },
          )}
          onClick={() => {
            setCurrentTabIndex(index + 1);
            onCurrentTabChange?.(index + 1);
          }}
        >
          {trigger}
        </div>
      ))}
      {extraContent}
    </section>
  );
};

export const TabsContent = memo(({ children }: { children: ReactNode[] }) => {
  const { currentTabIndex, tabsLength } = useTabsContext();
  const [translateX, setTranslateX] = useState<string>('');

  useEffect(() => {
    setTranslateX(
      currentTabIndex > 1
        ? `translateX(-${((currentTabIndex - 1) / tabsLength) * 100}%)`
        : '',
    );
  }, [currentTabIndex, tabsLength]);

  const validChildren = React.Children.toArray(children).filter(
    (child) => React.isValidElement(child) && child !== null,
  );
  return (
    <div className="flex-1 overflow-hidden w-full">
      <div
        style={{
          width: `${tabsLength * 100}%`,
          transform: translateX,
        }}
        className={cn(`lls-flex-row  h-full transition-all duration-500`)}
      >
        {validChildren.map((content, index) => (
          <section
            key={'content-list' + index}
            className={cn('w-full basis-1/1  px-5 py-3 h-full overflow-y-auto')}
          >
            {content}
          </section>
        ))}
      </div>
    </div>
  );
});
TabsContent.displayName = 'TabsContent';

 

posted @ 2025-06-16 13:46  send/me/a/cat  阅读(13)  评论(0)    收藏  举报