This commit is contained in:
@@ -1,139 +1,111 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import React, { ReactElement, ReactNode, useCallback, useState } from 'react'
|
||||
import HorizontalScrollView from '../listview/HorizontalScrollView'
|
||||
import { TabHeader, TabItemProps } from './TabHeader';
|
||||
import clsx from 'clsx';
|
||||
import TabHeaderRenderer from './TabHeaderRenderer';
|
||||
import Image from 'next/image';
|
||||
import { glassSurfaceNav } from '@/lib/styles/glassSurface';
|
||||
import { glassSurfaceNav } from "@/lib/styles/glassSurface";
|
||||
import clsx from "clsx";
|
||||
import Image from "next/image";
|
||||
import React, { ReactElement, ReactNode, useCallback, useState } from "react";
|
||||
import HorizontalScrollView from "../listview/HorizontalScrollView";
|
||||
import { TabHeader, TabItemProps } from "./TabHeader";
|
||||
import TabHeaderRenderer from "./TabHeaderRenderer";
|
||||
|
||||
type Props = {
|
||||
itemRenderer?: (child: ReactNode, index: number) => ReactElement;
|
||||
children: ReactNode;
|
||||
className?: TabContainerClassName;
|
||||
changeType?: TabContainerRenderType;
|
||||
defaultIndex?: number;
|
||||
selectedTab?: number;
|
||||
onTabChange?: (index: number) => void;
|
||||
} & Omit<React.OlHTMLAttributes<HTMLUListElement>, 'className'>;
|
||||
itemRenderer?: (child: ReactNode, index: number) => ReactElement;
|
||||
children: ReactNode;
|
||||
className?: TabContainerClassName;
|
||||
changeType?: TabContainerRenderType;
|
||||
defaultIndex?: number;
|
||||
selectedTab?: number;
|
||||
onTabChange?: (index: number) => void;
|
||||
} & Omit<React.OlHTMLAttributes<HTMLUListElement>, "className">;
|
||||
|
||||
export enum TabContainerRenderType {
|
||||
RENDERER,
|
||||
VISIBILITY,
|
||||
RENDERER,
|
||||
VISIBILITY,
|
||||
}
|
||||
|
||||
export type TabContainerClassName = {
|
||||
wrapper?: string;
|
||||
scrollView?: string;
|
||||
header?: string;
|
||||
headerDeactive?: string;
|
||||
headerActive?: string;
|
||||
icon?: string;
|
||||
iconDeactive?: string;
|
||||
iconActive?: string;
|
||||
title?: string;
|
||||
titleDeactive?: string;
|
||||
titleActive?: string
|
||||
}
|
||||
wrapper?: string;
|
||||
scrollView?: string;
|
||||
header?: string;
|
||||
headerDeactive?: string;
|
||||
headerActive?: string;
|
||||
icon?: string;
|
||||
iconDeactive?: string;
|
||||
iconActive?: string;
|
||||
title?: string;
|
||||
titleDeactive?: string;
|
||||
titleActive?: string;
|
||||
};
|
||||
|
||||
export const TabContainerClassNames: TabContainerClassName = {
|
||||
wrapper: 'h-full',
|
||||
scrollView: glassSurfaceNav('h-[72px] gradient-border !pt-3 !pb-2 w-full overflow-y-hidden justify-center rounded-[32px] inline-flex items-center gap-10'),
|
||||
header: '',
|
||||
headerDeactive: '',
|
||||
headerActive: '',
|
||||
icon: 'text-xs transition-all duration-150',
|
||||
iconDeactive: 'stroke-disabled-text',
|
||||
iconActive: 'stroke-primary dark:stroke-foreground',
|
||||
title: 'text-xs transition-all duration-150',
|
||||
titleDeactive: 'text-disabled-text',
|
||||
titleActive: 'text-primary dark:text-foreground'
|
||||
wrapper: "h-full",
|
||||
scrollView: glassSurfaceNav("h-[72px] gradient-border !pt-3 !pb-2 w-full overflow-y-hidden justify-center rounded-[32px] inline-flex items-center gap-10 !shadow-none"),
|
||||
header: "",
|
||||
headerDeactive: "",
|
||||
headerActive: "",
|
||||
icon: "text-xs transition-all duration-150",
|
||||
iconDeactive: "stroke-disabled-text",
|
||||
iconActive: "stroke-primary dark:stroke-foreground",
|
||||
title: "text-xs transition-all duration-150",
|
||||
titleDeactive: "text-disabled-text",
|
||||
titleActive: "text-primary dark:text-foreground",
|
||||
};
|
||||
|
||||
function TabContainer({
|
||||
className = TabContainerClassNames,
|
||||
changeType = TabContainerRenderType.RENDERER,
|
||||
defaultIndex = 0,
|
||||
selectedTab: controlledSelectedTab,
|
||||
onTabChange,
|
||||
itemRenderer,
|
||||
children,
|
||||
...restProps
|
||||
}: Props) {
|
||||
const [internalSelectedTab, setInternalSelectedTab] = useState(defaultIndex);
|
||||
const selectedTab = controlledSelectedTab !== undefined ? controlledSelectedTab : internalSelectedTab;
|
||||
|
||||
const updateTab = useCallback(
|
||||
(index: number) => {
|
||||
if (onTabChange) {
|
||||
onTabChange(index);
|
||||
} else {
|
||||
setInternalSelectedTab(index);
|
||||
}
|
||||
},
|
||||
[onTabChange],
|
||||
);
|
||||
|
||||
const items = React.Children.toArray(children).filter((child): child is ReactElement<TabItemProps> => React.isValidElement(child) && child.type === TabHeader);
|
||||
|
||||
return (
|
||||
<div className={className.wrapper}>
|
||||
<HorizontalScrollView className={className.scrollView} itemRenderer={itemRenderer} {...restProps}>
|
||||
{items.map((item, index) => {
|
||||
const { title, imageSrc, icon }: TabItemProps = item.props;
|
||||
return (
|
||||
<TabHeaderRenderer key={index} className={clsx(className.header, index === selectedTab ? className.headerActive : className.headerDeactive)} onClick={() => updateTab(index)}>
|
||||
{imageSrc && <Image priority src={imageSrc} width={24} height={24} alt="category image" />}
|
||||
{icon && <span className={clsx(className.icon, index === selectedTab ? className.iconActive : className.iconDeactive)}>{icon}</span>}
|
||||
<h6 className={clsx(className.title, index === selectedTab ? className.titleActive : className.titleDeactive)}>{title}</h6>
|
||||
</TabHeaderRenderer>
|
||||
);
|
||||
})}
|
||||
</HorizontalScrollView>
|
||||
|
||||
{changeType === TabContainerRenderType.RENDERER && <div>{items[selectedTab]?.props?.viewRenderer ?? <div></div>}</div>}
|
||||
{changeType === TabContainerRenderType.VISIBILITY && (
|
||||
<>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={clsx(index === selectedTab ? "visible" : "hidden")}>
|
||||
{item.props.viewRenderer}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TabContainer({ className = TabContainerClassNames, changeType = TabContainerRenderType.RENDERER, defaultIndex = 0, selectedTab: controlledSelectedTab, onTabChange, itemRenderer, children, ...restProps }: Props) {
|
||||
|
||||
const [internalSelectedTab, setInternalSelectedTab] = useState(defaultIndex);
|
||||
const selectedTab = controlledSelectedTab !== undefined ? controlledSelectedTab : internalSelectedTab;
|
||||
|
||||
const updateTab = useCallback((index: number) => {
|
||||
if (onTabChange) {
|
||||
onTabChange(index);
|
||||
} else {
|
||||
setInternalSelectedTab(index);
|
||||
}
|
||||
}, [onTabChange]);
|
||||
|
||||
const items = React.Children.toArray(children).filter(
|
||||
(child): child is ReactElement<TabItemProps> =>
|
||||
React.isValidElement(child) && child.type === TabHeader
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={className.wrapper}>
|
||||
<HorizontalScrollView
|
||||
className={className.scrollView}
|
||||
itemRenderer={itemRenderer}
|
||||
{...restProps}>
|
||||
{items.map((item, index) => {
|
||||
const { title, imageSrc, icon }: TabItemProps = item.props;
|
||||
return (
|
||||
<TabHeaderRenderer
|
||||
key={index}
|
||||
className={clsx(
|
||||
className.header,
|
||||
index === selectedTab ? className.headerActive : className.headerDeactive
|
||||
)}
|
||||
onClick={() => updateTab(index)}
|
||||
>
|
||||
{imageSrc &&
|
||||
<Image
|
||||
priority
|
||||
src={imageSrc}
|
||||
width={24}
|
||||
height={24}
|
||||
alt="category image"
|
||||
/>
|
||||
}
|
||||
{icon &&
|
||||
<span className={clsx(
|
||||
className.icon,
|
||||
index === selectedTab ? className.iconActive : className.iconDeactive
|
||||
)}>
|
||||
{icon}
|
||||
</span>
|
||||
}
|
||||
<h6 className={clsx(
|
||||
className.title,
|
||||
index === selectedTab ? className.titleActive : className.titleDeactive
|
||||
)}>
|
||||
{title}
|
||||
</h6>
|
||||
</TabHeaderRenderer>
|
||||
)
|
||||
})}
|
||||
</HorizontalScrollView>
|
||||
|
||||
{changeType === TabContainerRenderType.RENDERER &&
|
||||
<div>
|
||||
{items[selectedTab]?.props?.viewRenderer ?? <div></div>}
|
||||
</div>
|
||||
}
|
||||
{changeType === TabContainerRenderType.VISIBILITY &&
|
||||
<>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={clsx(
|
||||
index === selectedTab ? 'visible' : 'hidden'
|
||||
)}>
|
||||
{item.props.viewRenderer}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TabContainer
|
||||
export default TabContainer;
|
||||
|
||||
Reference in New Issue
Block a user