fix: make all apps category tab scrollable (#5113)
* feat: make left and right button workable * refactor: separate category tab into a seperate component to reduce unnecessary re-rendering while scrolling through the arrow buttons. Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: alannnc <alannnc@gmail.com>pull/5132/head^2
parent
bb45b9617b
commit
bc6f959c7a
|
@ -38,13 +38,95 @@ export function useShouldShowArrows() {
|
|||
|
||||
type AllAppsPropsType = { apps: (App & { credentials: Credential[] | undefined })[] };
|
||||
|
||||
export default function AllApps({ apps }: AllAppsPropsType) {
|
||||
interface CategoryTabProps {
|
||||
selectedCategory: string | null;
|
||||
categories: string[];
|
||||
}
|
||||
|
||||
function CategoryTab({ selectedCategory, categories }: CategoryTabProps) {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const { ref, calculateScroll, leftVisible, rightVisible } = useShouldShowArrows();
|
||||
const handleLeft = () => {
|
||||
if (ref.current) {
|
||||
ref.current.scrollLeft -= 100;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRight = () => {
|
||||
if (ref.current) {
|
||||
ref.current.scrollLeft += 100;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative mb-4 flex flex-col justify-between lg:flex-row lg:items-center">
|
||||
<h2 className="text-lg font-semibold text-gray-900 ">
|
||||
{t("explore_apps", {
|
||||
category:
|
||||
(selectedCategory && selectedCategory[0].toUpperCase() + selectedCategory.slice(1)) ||
|
||||
t("all_apps"),
|
||||
})}
|
||||
</h2>
|
||||
{leftVisible && (
|
||||
<button onClick={handleLeft} className="absolute top-9 flex md:left-1/2 md:-top-1">
|
||||
<div className="flex h-12 w-5 items-center justify-end bg-white">
|
||||
<ChevronLeft className="h-4 w-4 text-gray-500" />
|
||||
</div>
|
||||
<div className="flex h-12 w-5 bg-gradient-to-l from-transparent to-white" />
|
||||
</button>
|
||||
)}
|
||||
<ul
|
||||
className="no-scrollbar mt-3 flex max-w-full space-x-1 overflow-x-auto lg:mt-0 lg:max-w-[50%]"
|
||||
onScroll={(e) => calculateScroll(e)}
|
||||
ref={ref}>
|
||||
<li
|
||||
onClick={() => {
|
||||
router.replace(router.asPath.split("?")[0], undefined, { shallow: true });
|
||||
}}
|
||||
className={classNames(
|
||||
selectedCategory === null ? "bg-gray-900 text-gray-50" : "bg-gray-50 text-gray-900",
|
||||
"rounded-md px-4 py-2.5 text-sm font-medium hover:cursor-pointer hover:bg-gray-900 hover:text-gray-50"
|
||||
)}>
|
||||
{t("all_apps")}
|
||||
</li>
|
||||
{categories.map((cat, pos) => (
|
||||
<li
|
||||
key={pos}
|
||||
onClick={() => {
|
||||
if (selectedCategory === cat) {
|
||||
router.replace(router.asPath.split("?")[0], undefined, { shallow: true });
|
||||
} else {
|
||||
router.replace(router.asPath.split("?")[0] + `?category=${cat}`, undefined, {
|
||||
shallow: true,
|
||||
});
|
||||
}
|
||||
}}
|
||||
className={classNames(
|
||||
selectedCategory === cat ? "bg-gray-900 text-gray-50" : "bg-gray-50 text-gray-900",
|
||||
"rounded-md px-4 py-2.5 text-sm font-medium hover:cursor-pointer hover:bg-gray-900 hover:text-gray-50"
|
||||
)}>
|
||||
{cat[0].toUpperCase() + cat.slice(1)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{rightVisible && (
|
||||
<button onClick={handleRight} className="absolute top-9 right-0 flex md:-top-1">
|
||||
<div className="flex h-12 w-5 bg-gradient-to-r from-transparent to-white" />
|
||||
<div className="flex h-12 w-5 items-center justify-end bg-white">
|
||||
<ChevronRight className="h-4 w-4 text-gray-500" />
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AllApps({ apps }: AllAppsPropsType) {
|
||||
const router = useRouter();
|
||||
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [appsContainerRef] = useAutoAnimate<HTMLDivElement>();
|
||||
const { ref, calculateScroll, leftVisible, rightVisible } = useShouldShowArrows();
|
||||
|
||||
const categories = apps
|
||||
.map((app) => app.category)
|
||||
.filter((cat, pos, self) => {
|
||||
|
@ -61,65 +143,7 @@ export default function AllApps({ apps }: AllAppsPropsType) {
|
|||
|
||||
return (
|
||||
<div className="mb-16">
|
||||
<div className="relative mb-4 flex flex-col justify-between lg:flex-row lg:items-center">
|
||||
<h2 className="text-lg font-semibold text-gray-900 ">
|
||||
{t("explore_apps", {
|
||||
category:
|
||||
(selectedCategory && selectedCategory[0].toUpperCase() + selectedCategory.slice(1)) ||
|
||||
t("all_apps"),
|
||||
})}
|
||||
</h2>
|
||||
{leftVisible && (
|
||||
<div className="absolute top-9 flex md:left-1/2 md:-top-1">
|
||||
<div className="flex h-12 w-5 items-center justify-end bg-white">
|
||||
<ChevronLeft className="h-4 w-4 text-gray-500" />
|
||||
</div>
|
||||
<div className="flex h-12 w-5 bg-gradient-to-l from-transparent to-white" />
|
||||
</div>
|
||||
)}
|
||||
<ul
|
||||
className="no-scrollbar mt-3 flex max-w-full space-x-1 overflow-x-auto lg:mt-0 lg:max-w-[50%]"
|
||||
onScroll={(e) => calculateScroll(e)}
|
||||
ref={ref}>
|
||||
<li
|
||||
onClick={() => {
|
||||
router.replace(router.asPath.split("?")[0], undefined, { shallow: true });
|
||||
}}
|
||||
className={classNames(
|
||||
selectedCategory === null ? "bg-gray-900 text-gray-50" : "bg-gray-50 text-gray-900",
|
||||
"rounded-md px-4 py-2.5 text-sm font-medium hover:cursor-pointer hover:bg-gray-900 hover:text-gray-50"
|
||||
)}>
|
||||
{t("all_apps")}
|
||||
</li>
|
||||
{categories.map((cat, pos) => (
|
||||
<li
|
||||
key={pos}
|
||||
onClick={() => {
|
||||
if (selectedCategory === cat) {
|
||||
router.replace(router.asPath.split("?")[0], undefined, { shallow: true });
|
||||
} else {
|
||||
router.replace(router.asPath.split("?")[0] + `?category=${cat}`, undefined, {
|
||||
shallow: true,
|
||||
});
|
||||
}
|
||||
}}
|
||||
className={classNames(
|
||||
selectedCategory === cat ? "bg-gray-900 text-gray-50" : "bg-gray-50 text-gray-900",
|
||||
"rounded-md px-4 py-2.5 text-sm font-medium hover:cursor-pointer hover:bg-gray-900 hover:text-gray-50"
|
||||
)}>
|
||||
{cat[0].toUpperCase() + cat.slice(1)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{rightVisible && (
|
||||
<div className="absolute top-9 right-0 flex md:-top-1">
|
||||
<div className="flex h-12 w-5 bg-gradient-to-r from-transparent to-white" />
|
||||
<div className="flex h-12 w-5 items-center justify-end bg-white">
|
||||
<ChevronRight className="h-4 w-4 text-gray-500" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CategoryTab selectedCategory={selectedCategory} categories={categories} />
|
||||
<div
|
||||
className="grid gap-3 lg:grid-cols-4 [@media(max-width:1270px)]:grid-cols-3 [@media(max-width:730px)]:grid-cols-2 [@media(max-width:500px)]:grid-cols-1"
|
||||
ref={appsContainerRef}>
|
||||
|
|
Loading…
Reference in New Issue