-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: wip sidebar * fix: build fix * feat: search & category sections & style changes * feat: carousel & search icon & hovers * feat: cutaways + scrollbar dismiss + search layout + mobile layout * feat: arrows for scroll + small fixes * feat: imp of useSearch + small changes * fix: small fix * feat: filterBar + sorting * fix: small fix * fix: style changes * fix: mobile style fixes * feat: first round of apps * fix: type * fix: menu * entrance anim * feat: styles updates * fix: small change * fix: mobile add card imp * feat: add description to apps * feat: add icons * feat: wip sidebar * fix: build fix * feat: search & category sections & style changes * feat: carousel & search icon & hovers * feat: cutaways + scrollbar dismiss + search layout + mobile layout * feat: arrows for scroll + small fixes * feat: imp of useSearch + small changes * fix: small fix * feat: filterBar + sorting * fix: small fix * fix: style changes * fix: mobile style fixes * feat: first round of apps * fix: type * fix: menu * entrance anim * feat: styles updates * fix: small change * fix: mobile add card imp * feat: add description to apps * feat: add icons * fix: featured apps * fix: upd images * feat: changed sizes + hovers * feat: small changes * fix: assets * fix: some spacing * fix: design change * chore: remove featured app label * feat: randomize integration apps * feat: moved About to the end of menu * feat: shuffle all apps * feat: carousel with controls * chore: ts fix * chore: route fix * feat: change some descriptin and catrgory name * chore: type fix * fix: animation with search * feat: upd copy * feat: redirect * fix: animation bug * feat: mobile fixes * fix: img --------- Co-authored-by: Majorfi <[email protected]> Co-authored-by: Major <[email protected]> Co-authored-by: Daniil Polienko <[email protected]>
- Loading branch information
1 parent
b9bdde5
commit 68be6d4
Showing
56 changed files
with
2,106 additions
and
306 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import {type ReactElement} from 'react'; | ||
import {cl} from '@builtbymom/web3/utils'; | ||
|
||
type TCarouselControlsProps = { | ||
carouselLength?: number; | ||
onDotsClick: (destination: number) => void; | ||
currentPage: number; | ||
}; | ||
|
||
export function CarouselControls({ | ||
carouselLength = 0, | ||
onDotsClick, | ||
currentPage | ||
}: TCarouselControlsProps): ReactElement | null { | ||
const numberOfControls = Math.ceil(carouselLength / 4); | ||
|
||
if (carouselLength && carouselLength < 5) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<div className={'hidden w-full justify-center md:flex'}> | ||
<div className={'flex gap-x-3'}> | ||
{Array(numberOfControls) | ||
.fill('') | ||
.map((_, index) => ( | ||
<button | ||
key={index} | ||
className={'p-[2px]'} | ||
onClick={() => { | ||
onDotsClick(index + 1); | ||
}}> | ||
<div | ||
className={cl( | ||
'size-2 rounded-full', | ||
currentPage === index + 1 ? 'bg-white' : 'bg-gray-500' | ||
)} | ||
/> | ||
</button> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import {cl} from '@builtbymom/web3/utils'; | ||
|
||
import {IconChevron} from './icons/IconChevron'; | ||
|
||
import type {ReactElement} from 'react'; | ||
|
||
type TCarouselSlideArrowsProps = { | ||
onScrollBack?: VoidFunction; | ||
onScrollForward?: VoidFunction; | ||
className?: string; | ||
}; | ||
|
||
export function CarouselSlideArrows({ | ||
onScrollBack, | ||
onScrollForward, | ||
className | ||
}: TCarouselSlideArrowsProps): ReactElement { | ||
return ( | ||
<div className={cl('flex w-full justify-between', className)}> | ||
<div /> | ||
<div className={'hidden gap-3 md:flex'}> | ||
<button | ||
onClick={onScrollBack} | ||
className={ | ||
'flex !h-8 items-center rounded-[4px] px-4 text-white outline !outline-1 outline-gray-600/50 hover:bg-gray-600/40' | ||
}> | ||
<IconChevron className={'size-3 rotate-90'} /> | ||
</button> | ||
<button | ||
onClick={onScrollForward} | ||
className={ | ||
'flex !h-8 items-center rounded-[4px] px-4 text-white outline !outline-1 outline-gray-600/50 hover:bg-gray-600/40' | ||
}> | ||
<IconChevron className={'size-3 -rotate-90'} /> | ||
</button> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import Image from 'next/image'; | ||
import Link from 'next/link'; | ||
import {IconShare} from '@common/icons/IconShare'; | ||
|
||
import type {ReactElement} from 'react'; | ||
import type {TApp} from '@common/types/category'; | ||
|
||
type TAppCardProps = { | ||
app: TApp; | ||
}; | ||
|
||
export function AppCard(props: TAppCardProps): ReactElement { | ||
return ( | ||
<> | ||
<Link | ||
href={props.app.appURI ?? ''} | ||
target={'_blank'} | ||
className={ | ||
'bg-grey-900 group relative hidden h-[240px] min-w-[208px] max-w-[208px] overflow-hidden rounded-lg border border-gray-700/50 p-6 hover:bg-gray-600/40 md:block' | ||
}> | ||
<div className={'mb-4'}> | ||
<div | ||
className={ | ||
'absolute right-2 top-2 hidden size-10 items-center justify-center rounded-lg bg-gray-900 group-hover:flex' | ||
}> | ||
<IconShare className={'size-[10px]'} /> | ||
</div> | ||
{props.app.logoURI ? ( | ||
<Image | ||
src={props.app.logoURI} | ||
alt={props.app.name} | ||
unoptimized | ||
width={240} | ||
height={240} | ||
className={'size-[80px] rounded-full border border-[#292929]/80 object-contain'} | ||
/> | ||
) : ( | ||
<div className={'size-[80px] rounded-full bg-fallback'} /> | ||
)} | ||
</div> | ||
<div className={'mb-1 text-lg font-bold text-white'}>{props.app.name}</div> | ||
|
||
<p className={'max-h-[60px] whitespace-normal text-sm text-gray-400'}>{props.app.description}</p> | ||
</Link> | ||
<Link | ||
href={props.app.appURI} | ||
className={'flex items-center md:hidden'}> | ||
<div> | ||
{props.app.logoURI ? ( | ||
<div className={'size-16 rounded-[32px]'}> | ||
<Image | ||
src={props.app.logoURI} | ||
alt={props.app.name} | ||
width={300} | ||
height={300} | ||
unoptimized | ||
className={'size-full rounded-2xl bg-center object-cover md:rounded-[32px]'} | ||
/> | ||
</div> | ||
) : ( | ||
<div className={'size-16 rounded-2xl bg-fallback md:rounded-[32px]'} /> | ||
)} | ||
</div> | ||
|
||
<div className={'ml-4'}> | ||
<div className={'mb-1 text-base font-bold text-gray-300'}>{props.app.name}</div> | ||
<p className={'line-clamp-2 h-12 text-xs text-gray-400 md:text-base'}>{props.app.description}</p> | ||
</div> | ||
</Link> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import {type ForwardedRef, forwardRef, type ReactElement} from 'react'; | ||
import React from 'react'; | ||
import {cl} from '@builtbymom/web3/utils'; | ||
|
||
import {AppCard} from './AppCard'; | ||
import {FeaturedApp} from './FeaturedApp'; | ||
|
||
import type {TApp} from '@common/types/category'; | ||
|
||
export const AppsCarousel = forwardRef( | ||
( | ||
props: {onScroll?: VoidFunction; isUsingFeatured?: boolean; apps: TApp[]}, | ||
ref: ForwardedRef<HTMLDivElement> | ||
): ReactElement => { | ||
return ( | ||
<div className={props.isUsingFeatured ? 'h-[262px]' : 'h-[360px] md:h-[262px]'}> | ||
<section className={'absolute left-0 -mx-1 w-full'}> | ||
<div | ||
className={ | ||
'pointer-events-none absolute left-0 top-0 z-30 h-[272px] w-1/6 bg-gradient-to-r from-gray-900/0 to-transparent md:h-full' | ||
} | ||
/> | ||
<div | ||
className={ | ||
'pointer-events-none absolute right-0 top-0 z-30 h-[272px] w-1/5 bg-gradient-to-l from-gray-900/0 to-transparent md:h-full' | ||
} | ||
/> | ||
<div | ||
ref={ref} | ||
onScroll={props.onScroll} | ||
className={cl( | ||
'hidden md:flex overflow-x-auto pb-1 pl-[38px] scrollbar-none max-sm:pr-6', | ||
props.isUsingFeatured ? 'gap-x-8' : 'flex-col md:flex-row gap-x-4 overflow-y-hidden' | ||
)}> | ||
{props.apps?.map((app, i) => { | ||
return ( | ||
<React.Fragment key={app.appURI + i}> | ||
{props.isUsingFeatured ? ( | ||
<FeaturedApp | ||
key={app.name + i} | ||
app={app} | ||
/> | ||
) : ( | ||
<AppCard | ||
app={app} | ||
key={app.name + i} | ||
/> | ||
)} | ||
</React.Fragment> | ||
); | ||
})} | ||
</div> | ||
<div | ||
ref={ref} | ||
onScroll={props.onScroll} | ||
className={cl( | ||
'flex md:hidden overflow-x-auto pb-1 pl-[38px] scrollbar-none max-sm:pr-6', | ||
props.isUsingFeatured ? 'gap-x-8' : 'flex-col md:flex-row gap-y-4 overflow-y-hidden' | ||
)}> | ||
{props.apps?.slice(0, 4).map((app, i) => { | ||
return ( | ||
<React.Fragment key={app.appURI + i}> | ||
{props.isUsingFeatured ? ( | ||
<FeaturedApp | ||
key={app.name + i} | ||
app={app} | ||
/> | ||
) : ( | ||
<AppCard | ||
app={app} | ||
key={app.name + i} | ||
/> | ||
)} | ||
</React.Fragment> | ||
); | ||
})} | ||
</div> | ||
</section> | ||
</div> | ||
); | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import {type ReactElement, useRef, useState} from 'react'; | ||
import {useMountEffect} from '@react-hookz/web'; | ||
import {CarouselControls} from '@common/CarouselControls'; | ||
import {CarouselSlideArrows} from '@common/CarouselSlideArrows'; | ||
import {IconShare} from '@common/icons/IconShare'; | ||
|
||
import {AppsCarousel} from './AppsCarousel'; | ||
|
||
import type {TApp} from '@common/types/category'; | ||
|
||
type TAppSectionProps = { | ||
title: string; | ||
onExpandClick: () => void; | ||
apps: TApp[]; | ||
}; | ||
|
||
export const CategorySection = ({title, onExpandClick, apps}: TAppSectionProps): ReactElement => { | ||
const [shuffledApps, set_shuffledApps] = useState<TApp[]>([]); | ||
const [currentPage, set_currentPage] = useState(1); | ||
const carouselRef = useRef<HTMLDivElement | null>(null); | ||
const [isProgrammaticScroll, set_isProgrammaticScroll] = useState(false); | ||
|
||
/********************************************************************************************** | ||
** Handles scrolling back to the previous page in the carousel. | ||
** It updates the scroll position, current page, and sets a flag to indicate programmatic | ||
** scrolling. The flag is reset after a delay to allow for smooth scrolling. | ||
*********************************************************************************************/ | ||
const onScrollBack = (): void => { | ||
if (!carouselRef.current || currentPage === 1) return; | ||
set_isProgrammaticScroll(true); | ||
carouselRef.current.scrollLeft -= 880; | ||
set_currentPage(prev => prev - 1); | ||
|
||
setTimeout(() => { | ||
set_isProgrammaticScroll(false); | ||
}, 3000); | ||
}; | ||
|
||
/********************************************************************************************** | ||
** Handles scrolling forward to the next page in the carousel. | ||
** It updates the scroll position, current page, and sets a flag to indicate programmatic | ||
** scrolling. The flag is reset after a delay to allow for smooth scrolling. | ||
*********************************************************************************************/ | ||
const onScrollForward = (): void => { | ||
if (!carouselRef.current || currentPage === Math.ceil(apps.length / 4)) return; | ||
set_isProgrammaticScroll(true); | ||
carouselRef.current.scrollLeft += 880; | ||
set_currentPage(prev => prev + 1); | ||
|
||
setTimeout(() => { | ||
set_isProgrammaticScroll(false); | ||
}, 3000); | ||
}; | ||
|
||
/********************************************************************************************** | ||
** Handles clicking on the carousel dots to navigate to a specific page. | ||
** It updates the scroll position, current page, and sets a flag to indicate programmatic | ||
** scrolling. The flag is reset after a delay to allow for smooth scrolling. | ||
*********************************************************************************************/ | ||
const onDotsClick = (destination: number): void => { | ||
if (!carouselRef.current || destination === currentPage) return; | ||
set_isProgrammaticScroll(true); | ||
if (destination > currentPage) { | ||
carouselRef.current.scrollLeft += 1000 * (destination - currentPage); | ||
setTimeout(() => { | ||
set_isProgrammaticScroll(false); | ||
}, 3000); | ||
} else { | ||
carouselRef.current.scrollLeft -= 1000 * (currentPage - destination); | ||
setTimeout(() => { | ||
set_isProgrammaticScroll(false); | ||
}, 3000); | ||
} | ||
set_currentPage(destination); | ||
}; | ||
|
||
/********************************************************************************************** | ||
** Handles the scroll event of the carousel. | ||
** It calculates the current page based on the scroll position and updates the state. | ||
** This function is not triggered during programmatic scrolling to avoid conflicts. | ||
*********************************************************************************************/ | ||
const onScroll = (): void => { | ||
if (!carouselRef.current || isProgrammaticScroll) return; | ||
const {scrollLeft} = carouselRef.current; | ||
const page = Math.ceil(scrollLeft / 1000) + 1; | ||
set_currentPage(page); | ||
}; | ||
|
||
/********************************************************************************************** | ||
** On component mount we shuffle the array of Partners to avoid any bias. | ||
**********************************************************************************************/ | ||
useMountEffect(() => { | ||
if (apps?.length < 1) { | ||
return; | ||
} | ||
set_shuffledApps(apps?.toSorted(() => 0.5 - Math.random())); | ||
}); | ||
return ( | ||
<div className={'flex flex-col overflow-hidden'}> | ||
<div className={'mb-6 flex h-10 w-full items-center justify-between pr-1'}> | ||
<div className={'flex gap-x-4'}> | ||
<div className={'whitespace-nowrap text-lg font-bold text-white'}>{title}</div> | ||
<button | ||
onClick={onExpandClick} | ||
className={ | ||
'flex items-center rounded-[4px] px-4 py-2 outline !outline-1 outline-gray-600/50 hover:bg-gray-600/40' | ||
}> | ||
<span className={'mr-2 whitespace-nowrap text-xs text-white'}>{'View all'}</span> | ||
<IconShare className={'size-3 text-white'} /> | ||
</button> | ||
</div> | ||
{apps?.length > 4 && ( | ||
<CarouselSlideArrows | ||
onScrollBack={onScrollBack} | ||
onScrollForward={onScrollForward} | ||
/> | ||
)} | ||
</div> | ||
<AppsCarousel | ||
apps={shuffledApps} | ||
ref={carouselRef} | ||
onScroll={onScroll} | ||
/> | ||
<CarouselControls | ||
carouselLength={apps.length} | ||
onDotsClick={onDotsClick} | ||
currentPage={currentPage} | ||
/> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.