Skip to content

Commit

Permalink
feat: add floating header and other statuses to project hero
Browse files Browse the repository at this point in the history
  • Loading branch information
1emu committed Jun 27, 2024
1 parent 16d118b commit ab3e22e
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 79 deletions.
2 changes: 1 addition & 1 deletion src/components/FloatingHeader/FloatingHeader.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
padding-left: 14px;
}

.FloatingHeader__Status {
.FloatingHeader__Body {
display: flex;
gap: 8px;
min-height: 24px;
Expand Down
15 changes: 5 additions & 10 deletions src/components/FloatingHeader/FloatingHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
import classNames from 'classnames'

import { ProposalAttributes } from '../../types/proposals'
import CategoryPill from '../Category/CategoryPill'
import WiderContainer from '../Common/WiderContainer'
import StatusPill from '../Status/StatusPill'

import './FloatingHeader.css'

interface FloatingHeaderProps {
isVisible: boolean
proposal: ProposalAttributes
title: string
children?: React.ReactNode
}

const FloatingHeader = ({ isVisible, proposal }: FloatingHeaderProps) => {
const FloatingHeader = ({ isVisible, title, children }: FloatingHeaderProps) => {
return (
<div className={classNames('FloatingHeader', !isVisible && 'FloatingHeader--hidden')}>
<WiderContainer className="FloatingHeader__Content">
<div className="FloatingHeader__Title">{proposal?.title}</div>
<div className="FloatingHeader__Status">
<StatusPill isLink status={proposal.status} />
<CategoryPill isLink proposalType={proposal.type} />
</div>
<div className="FloatingHeader__Title">{title}</div>
<div className="FloatingHeader__Body">{children}</div>
</WiderContainer>
<div className="FloatingHeader__Shadow"></div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
}

.ProjectHero__Title {
color: var(--white-900);
font-weight: var(--weight-semi-bold);
font-size: 18px;
line-height: 26px;
Expand All @@ -45,6 +44,10 @@
margin: 8px 0;
}

.ProjectHero__Title--active {
color: var(--white-900);
}

@media (min-width: 768px) {
.ProjectHero__Title {
margin-bottom: 0.3em;
Expand All @@ -57,11 +60,14 @@
}

.ProjectHero__Banner {
background: linear-gradient(90deg, #d80027 0%, #a524b3 100%);
box-shadow: none;
border-radius: 8px;
}

.ProjectHero__Banner--active {
background: linear-gradient(90deg, #d80027 0%, #a524b3 100%);
}

@media (min-width: 768px) {
.ProjectHero__Banner {
border-radius: 6px;
Expand Down
56 changes: 56 additions & 0 deletions src/components/Projects/ProjectHero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Ref, forwardRef } from 'react'

import classNames from 'classnames'
import { Loader } from 'decentraland-ui/dist/components/Loader/Loader'

import { ProjectStatus } from '../../types/grants.ts'
import { Project } from '../../types/proposals.ts'
import { PillColor } from '../Common/Pill.tsx'
import DotsMenu from '../Icon/DotsMenu.tsx'
import SlimCross from '../Icon/SlimCross.tsx'
import HeroBanner from '../Proposal/HeroBanner.tsx'

import './ProjectHero.css'
import ProjectViewStatusPill from './ProjectViewStatusPill.tsx'

const IS_DOTS_MENU_ENABLED = false

interface Props {
project: Project
onClose?: () => void
}

const HIGHLIGHTED_STATUSES = [ProjectStatus.Pending, ProjectStatus.InProgress]

const ProjectHero = forwardRef(({ project, onClose }: Props, ref: Ref<HTMLDivElement>) => {
const active = HIGHLIGHTED_STATUSES.includes(project.status)

return (
<>
<div className="ProjectHero__Container" ref={ref}>
<HeroBanner
active={active}
color={PillColor.Purple}
className={classNames(['ProjectHero__Banner', active && 'ProjectHero__Banner--active'])}
/>
<div className="ProjectHero__Text">
<h1 className={classNames(['ProjectHero__Title', active && 'ProjectHero__Title--active'])}>
{project?.title || ''}
</h1>
<Loader active={!project} />
{project && <ProjectViewStatusPill project={project} hero />}
</div>
<div className="ProjectHero__Menu">
{IS_DOTS_MENU_ENABLED && <DotsMenu color="var(--white-900)" className="ProjectView__MenuDots" />}
{onClose && (
<SlimCross size={14} color="var(--white-900)" onClick={onClose} className="ProjectView__MenuCross" />
)}
</div>
</div>
</>
)
})

ProjectHero.displayName = 'ProjectHero'

export default ProjectHero
12 changes: 7 additions & 5 deletions src/components/Projects/ProjectView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'
import { Ref, forwardRef, useMemo, useState } from 'react'

import useFormatMessage from '../../hooks/useFormatMessage'
import useShowProjectUpdatesCta from '../../hooks/useShowProjectUpdatesCta.ts'
Expand All @@ -12,17 +12,17 @@ import UpdatesTabView from './Updates/UpdatesTabView'

import MilestonesTab from './MilestonesTab'
import ProjectGeneralInfo from './ProjectGeneralInfo'
import ProjectHero from './ProjectHero'
import ProjectVerticalTab from './ProjectVerticalTab.tsx'
import './ProjectView.css'
import ProjectViewFundingSection from './ProjectViewFundingSection.tsx'
import ProjectViewTitle from './ProjectViewTitle'

interface Props {
project?: Project | null
onClose?: () => void
}

function ProjectView({ project, onClose }: Props) {
const ProjectView = forwardRef(({ project, onClose }: Props, ref: Ref<HTMLDivElement>) => {
const t = useFormatMessage()
const [viewIdx, setViewIdx] = useState(0)
const showMilestonesTab = !(
Expand Down Expand Up @@ -56,7 +56,7 @@ function ProjectView({ project, onClose }: Props) {

return (
<div className="ProjectView">
{project && <ProjectViewTitle project={project} onClose={onClose} />}
{project && <ProjectHero project={project} onClose={onClose} ref={ref} />}
{project && (
<Desktop1200>
<div className="ProjectView__Left">
Expand Down Expand Up @@ -89,6 +89,8 @@ function ProjectView({ project, onClose }: Props) {
</div>
</div>
)
}
})

ProjectView.displayName = 'ProjectView'

export default ProjectView
9 changes: 6 additions & 3 deletions src/components/Projects/ProjectViewStatusPill.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@
background: var(--black-200);
}

.ProjectViewStatusPill--revoked {
.ProjectViewStatusPill--revoked,
.ProjectViewStatusPill--revoked--hero {
color: var(--red-800);
background: var(--red-200);
}

.ProjectViewStatusPill--finished {
.ProjectViewStatusPill--finished,
.ProjectViewStatusPill--finished--hero {
color: var(--green-900);
background: var(--green-200);
}

.ProjectViewStatusPill--paused {
.ProjectViewStatusPill--paused,
.ProjectViewStatusPill--paused--hero {
color: var(--orange-800);
background: var(--orange-200);
}
56 changes: 0 additions & 56 deletions src/components/Projects/ProjectViewTitle.tsx

This file was deleted.

34 changes: 33 additions & 1 deletion src/pages/project.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
import { useEffect, useRef, useState } from 'react'

import { NotMobile } from 'decentraland-ui/dist/components/Media/Media'

import WiderContainer from '../components/Common/WiderContainer'
import FloatingHeader from '../components/FloatingHeader/FloatingHeader.tsx'
import Head from '../components/Layout/Head'
import LoadingView from '../components/Layout/LoadingView'
import Navigation, { NavigationTab } from '../components/Layout/Navigation'
import NotFound from '../components/Layout/NotFound'
import ProjectView from '../components/Projects/ProjectView'
import ProjectViewStatusPill from '../components/Projects/ProjectViewStatusPill.tsx'
import useProject from '../hooks/useProject'
import useURLSearchParams from '../hooks/useURLSearchParams'
import locations from '../utils/locations'

export default function ProjectPage() {
const params = useURLSearchParams()
const { project, isLoadingProject } = useProject(params.get('id'))
const [isFloatingHeaderVisible, setIsFloatingHeaderVisible] = useState<boolean>(true)
const heroSectionRef = useRef<HTMLDivElement | null>(null)

const title = project?.title || ''
const description = project?.about || ''

useEffect(() => {
setIsFloatingHeaderVisible(false)
if (!isLoadingProject && typeof window !== 'undefined') {
const handleScroll = () => {
if (!!heroSectionRef.current && !!window) {
const { top: heroSectionTop, height: heroSectionHeight } = heroSectionRef.current.getBoundingClientRect()
setIsFloatingHeaderVisible(heroSectionTop + heroSectionHeight / 2 < 0)
}
}

window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}
}, [isLoadingProject])

if (isLoadingProject) {
return (
<>
Expand All @@ -41,8 +66,15 @@ export default function ProjectPage() {
links={[{ rel: 'canonical', href: locations.project({ id: project?.id }) }]}
/>
<Navigation activeTab={NavigationTab.Projects} />
<NotMobile>
{project && (
<FloatingHeader isVisible={isFloatingHeaderVisible} title={project.title}>
<ProjectViewStatusPill project={project} />
</FloatingHeader>
)}
</NotMobile>
<WiderContainer>
<ProjectView project={project} />
<ProjectView project={project} ref={heroSectionRef} />
</WiderContainer>
</>
)
Expand Down
15 changes: 14 additions & 1 deletion src/pages/proposal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Desktop, NotMobile, TabletAndBelow } from 'decentraland-ui/dist/compone
import { ErrorClient } from '../clients/ErrorClient'
import { Governance } from '../clients/Governance'
import { SnapshotApi } from '../clients/SnapshotApi'
import CategoryPill from '../components/Category/CategoryPill.tsx'
import ProposalVPChart from '../components/Charts/ProposalVPChart'
import WiderContainer from '../components/Common/WiderContainer'
import FloatingBar from '../components/FloatingBar/FloatingBar'
Expand Down Expand Up @@ -43,6 +44,7 @@ import GovernanceProcess from '../components/Proposal/View/GovernanceProcess'
import ProposalDetailSection from '../components/Proposal/View/ProposalDetailSection'
import ProposalImagesPreview from '../components/Proposal/View/ProposalImagesPreview'
import ProposalMarkdown from '../components/Proposal/View/ProposalMarkdown'
import StatusPill from '../components/Status/StatusPill.tsx'
import { useAuthContext } from '../context/AuthProvider'
import { DEFAULT_QUERY_STALE_TIME } from '../hooks/constants'
import useAsyncTask from '../hooks/useAsyncTask'
Expand Down Expand Up @@ -360,7 +362,18 @@ export default function ProposalPage() {
links={[{ rel: 'canonical', href: locations.proposal(proposal?.id || '') }]}
/>
<Navigation activeTab={NavigationTab.Proposals} />
<NotMobile>{proposal && <FloatingHeader isVisible={isFloatingHeaderVisible} proposal={proposal} />}</NotMobile>
<NotMobile>
{proposal && (
<FloatingHeader isVisible={isFloatingHeaderVisible} title={proposal.title}>
{
<>
<StatusPill isLink status={proposal.status} />
<CategoryPill isLink proposalType={proposal.type} />
</>
}
</FloatingHeader>
)}
</NotMobile>
<WiderContainer className="ProposalDetailPage">
<ProposalHero proposal={proposal} ref={heroSectionRef} />
{proposal && (
Expand Down

0 comments on commit ab3e22e

Please sign in to comment.