diff --git a/gatsby-browser.js b/gatsby-browser.js index 1cd3e4032..c5cd8aa2b 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -20,9 +20,7 @@ import AuthProvider from 'decentraland-gatsby/dist/context/Auth/AuthProvider' import FeatureFlagProvider from 'decentraland-gatsby/dist/context/FeatureFlag/FeatureFlagProvider' import segment from 'decentraland-gatsby/dist/utils/segment/segment' import Layout from './src/components/Layout/Layout' -import Navbar from './src/components/Layout/Navbar' import IdentityModalContextProvider from './src/components/Context/IdentityModalContext' -import BurgerMenuStatusContextProvider from './src/components/Context/BurgerMenuStatusContext' import ExternalLinkWarningModal from './src/components/Modal/ExternalLinkWarningModal/ExternalLinkWarningModal' import IdentityConnectModal from './src/components/Modal/IdentityConnectModal/IdentityConnectModal' import Segment from "decentraland-gatsby/dist/components/Development/Segment" @@ -30,6 +28,7 @@ import { SEGMENT_KEY, SSO_URL } from "./src/constants" import { flattenMessages } from "./src/utils/intl" import en from "./src/intl/en.json" import SnapshotStatus from "./src/components/Debug/SnapshotStatus" +import UserInformation from 'decentraland-gatsby/dist/components/User/UserInformation' const queryClient = new QueryClient() @@ -47,12 +46,10 @@ export const wrapPageElement = ({ element, props }) => { - - }> + }> {element} - diff --git a/src/components/Banner/Banner.css b/src/components/Banner/Banner.css index 144b05e37..46f406d3f 100644 --- a/src/components/Banner/Banner.css +++ b/src/components/Banner/Banner.css @@ -4,6 +4,7 @@ align-content: stretch; position: relative; padding: 16px 16px 16px 20px; + margin-top: 24px; margin-bottom: 8px; border-radius: 12px; outline: 1px solid #0000000a; @@ -42,10 +43,6 @@ } @media (max-width: 767px) { - .OnlyMobile .Banner { - display: flex; - } - .Banner .Banner__Content { display: contents; flex-direction: column; @@ -62,7 +59,7 @@ z-index: 1; } - .OnlyMobile .Banner .Banner__Button { + .Banner .Banner__Button { padding-left: 0; padding-right: 0; min-width: 87px; @@ -71,6 +68,7 @@ @media (min-width: 768px) { .Banner { + margin-top: 0; margin-bottom: 35px; } diff --git a/src/components/Category/CategoryList.tsx b/src/components/Category/CategoryList.tsx index 50c689bdb..8e9edb1ee 100644 --- a/src/components/Category/CategoryList.tsx +++ b/src/components/Category/CategoryList.tsx @@ -5,7 +5,6 @@ import { Header } from 'decentraland-ui/dist/components/Header/Header' import { ProposalType, toProposalType } from '../../entities/Proposal/types' import { getUrlFilters } from '../../helpers' -import { useBurgerMenu } from '../../hooks/useBurgerMenu' import useFormatMessage from '../../hooks/useFormatMessage' import ActionableLayout from '../Layout/ActionableLayout' @@ -18,20 +17,10 @@ function CategoryList() { const location = useLocation() const params = useMemo(() => new URLSearchParams(location.search), [location.search]) const type = toProposalType(params.get('type')) ?? undefined - const { setStatus } = useBurgerMenu() - - function handleClick() { - setStatus((prevState) => ({ ...prevState, open: false })) - } return ( {t(`page.proposal_list.categories`)}}> - + {(Object.keys(ProposalType) as Array).map((key, index) => { return ( ) })} diff --git a/src/components/Category/CategoryOption.tsx b/src/components/Category/CategoryOption.tsx index c782e4ce0..b04df6731 100644 --- a/src/components/Category/CategoryOption.tsx +++ b/src/components/Category/CategoryOption.tsx @@ -122,7 +122,9 @@ export default function CategoryOption({ active, type, className, count, subtype {getCategoryIcon(type, CategoryIconVariant.Circled)} - {t(`category.${type}_title`)} + + {t(`category.${type}_title`)} + {hasSubtypes && ( > -} - -export const BurgerMenuStatusContext = createContext({} as BurgerMenuStatusContextType) - -const BurgerMenuStatusContextProvider = ({ children }: BurgerMenuStatusContextProps) => { - const searchParams = useProposalsSearchParams() - const filtering = - !!searchParams.type || !!searchParams.status || (!!searchParams.timeFrame && searchParams.timeFrame.length > 0) - const [status, setStatus] = useState({ - open: searchParams.searching || filtering, - searching: searchParams.searching, - filtering: filtering, - snapshotStatusBarOpen: false, - }) - return {children} -} - -export default BurgerMenuStatusContextProvider diff --git a/src/components/Debug/SnapshotStatus.css b/src/components/Debug/SnapshotStatus.css index 8240cf8a0..de3816056 100644 --- a/src/components/Debug/SnapshotStatus.css +++ b/src/components/Debug/SnapshotStatus.css @@ -6,7 +6,7 @@ justify-content: center; align-items: center; overflow: hidden; - z-index: 21; + z-index: 10; position: relative; transition: all 0.75s ease 0s; padding-left: 26px; diff --git a/src/components/Debug/SnapshotStatus.tsx b/src/components/Debug/SnapshotStatus.tsx index 1d2edc054..0cbdb2b91 100644 --- a/src/components/Debug/SnapshotStatus.tsx +++ b/src/components/Debug/SnapshotStatus.tsx @@ -5,7 +5,6 @@ import classNames from 'classnames' import { Governance } from '../../clients/Governance' import { ServiceHealth, SnapshotStatus as SnapshotServiceStatus } from '../../clients/SnapshotTypes' import { SNAPSHOT_STATUS_ENABLED } from '../../constants' -import { useBurgerMenu } from '../../hooks/useBurgerMenu' import useFormatMessage from '../../hooks/useFormatMessage' import Markdown from '../Common/Typography/Markdown' import WarningTriangle from '../Icon/WarningTriangle' @@ -23,7 +22,6 @@ function logIfNotNormal(status: SnapshotServiceStatus) { export default function SnapshotStatus() { const t = useFormatMessage() const [showTopBar, setShowTopBar] = useState(false) - const { setStatus } = useBurgerMenu() const [ping, setPing] = useState(false) const updateServiceStatus = async () => { @@ -31,7 +29,6 @@ export default function SnapshotStatus() { logIfNotNormal(status) const show = status.scoresStatus.health === ServiceHealth.Failing && SNAPSHOT_STATUS_ENABLED setShowTopBar(show) - setStatus((prev) => ({ ...prev, snapshotStatusBarOpen: show })) } useEffect(() => { diff --git a/src/components/Home/Charts.css b/src/components/Home/Charts.css index d12834749..cd5bcfae5 100644 --- a/src/components/Home/Charts.css +++ b/src/components/Home/Charts.css @@ -2,71 +2,14 @@ width: 100%; } -.ui.card.HomeCharts > .dcl.tabs { - padding: 0 18px; - margin: 0; - display: flex !important; - height: unset; -} - -.ui.card.HomeCharts > .dcl.tabs .tabs-left { - display: flex; - justify-content: space-evenly; -} - -.ui.card.HomeCharts > .dcl.tabs .tabs-right { - display: none; -} - -.ui.card.HomeCharts > .dcl.tabs .dcl.tab { - padding: 3px 0; - margin: 0; - font-size: 11px; - font-weight: var(--weight-semi-bold); - text-transform: uppercase; - color: var(--black-600); -} - -.ui.card.HomeCharts > .dcl.tabs .dcl.tab.active { - border-left: none; - border-bottom: 2px solid var(--primary); -} - -.ui.card.HomeCharts > .dcl.tabs .Display { - margin: 0; +.HomeCharts__Display.ui.sub.header { + margin: 0 !important; } .ui.card.HomeCharts > canvas { padding: 12px; } -@media (max-width: 425px) { - .ui.card.HomeCharts > .dcl.tabs { - padding: 0 0; - } -} - -@media screen and (min-width: 1024px) { - .ui.card.HomeCharts > .dcl.tabs .tabs-left { - display: unset; - } - - .ui.card.HomeCharts > .dcl.tabs .tabs-right { - display: flex; - } - - .ui.card.HomeCharts > .dcl.tabs .dcl.tab { - padding: 15px 0; - margin-right: 32px; - font-size: 13px; - line-height: 17px; - } - - .ui.card.HomeCharts > .dcl.tabs .dcl.tab.active { - border-bottom: unset; - } -} - .Charts__Loader { display: flex; justify-content: center; diff --git a/src/components/Home/Charts.tsx b/src/components/Home/Charts.tsx index 71445a827..6faa2b537 100644 --- a/src/components/Home/Charts.tsx +++ b/src/components/Home/Charts.tsx @@ -2,12 +2,13 @@ import { useState } from 'react' import { Card } from 'decentraland-ui/dist/components/Card/Card' import { Header } from 'decentraland-ui/dist/components/Header/Header' -import { Tabs } from 'decentraland-ui/dist/components/Tabs/Tabs' +import { Desktop } from 'decentraland-ui/dist/components/Media/Media' import useFormatMessage from '../../hooks/useFormatMessage' import useParticipatingVP from '../../hooks/useParticipatingVP' import useVotesPerProposal from '../../hooks/useVotesPerProposal' import LineChart from '../Charts/LineChart' +import BoxTabs from '../Common/BoxTabs' import './Charts.css' import HomeLoader from './HomeLoader' @@ -32,21 +33,26 @@ function Charts() { return ( - - - setSelectedTab(ChartType.ParticipatingVP)}> + + + setSelectedTab(ChartType.ParticipatingVP)}> {t('page.home.community_engagement.participating_vp_title')} - - setSelectedTab(ChartType.VotesPerProposal)}> + + setSelectedTab(ChartType.VotesPerProposal)} + > {t('page.home.community_engagement.votes_per_proposal_title')} - - - -
- {t('page.home.community_engagement.display_median')} -
-
-
+ + + + +
+ {t('page.home.community_engagement.display_median')} +
+
+
+ {((isSelectedParticipatingVPTab && isLoadingParticipatingVP) || (isSelectedVotesPerProposalTab && isLoadingVotesPerProposal)) && (
diff --git a/src/components/Icon/Cross.tsx b/src/components/Icon/Cross.tsx index 2baa5f933..67fa4cc08 100644 --- a/src/components/Icon/Cross.tsx +++ b/src/components/Icon/Cross.tsx @@ -1,9 +1,20 @@ -function Cross({ className }: { className?: string }) { +function Cross({ + className, + onClick, + size = '10', + color = 'var(--black-600)', +}: { + className?: string + onClick?: () => void + size?: string + color?: string +}) { return ( ) diff --git a/src/components/Icon/Filter.tsx b/src/components/Icon/Filter.tsx new file mode 100644 index 000000000..322da6644 --- /dev/null +++ b/src/components/Icon/Filter.tsx @@ -0,0 +1,10 @@ +export default function Filter() { + return ( + + + + ) +} diff --git a/src/components/Icon/Magnifiy.tsx b/src/components/Icon/Magnifiy.tsx new file mode 100644 index 000000000..4ab253ce4 --- /dev/null +++ b/src/components/Icon/Magnifiy.tsx @@ -0,0 +1,19 @@ +function Magnify() { + return ( + + + + + + + + + + + ) +} + +export default Magnify diff --git a/src/components/Layout/ActionableLayout.css b/src/components/Layout/ActionableLayout.css index 0f23245c6..06c635f75 100644 --- a/src/components/Layout/ActionableLayout.css +++ b/src/components/Layout/ActionableLayout.css @@ -1,16 +1,23 @@ -.ActionableLayout .ActionableLayout__Action { +.ActionableLayout__Action { min-height: 32px; display: flex; flex-wrap: wrap; justify-content: space-between; - align-items: center + align-items: center; } -.ActionableLayout .ActionableLayout__Content { - padding-top: .5rem; - height: calc(100% - 32px); +@media (min-width: 768px) { + .ActionableLayout__Action { + margin-bottom: 16px; + } +} + +.ActionableLayout__Right { + display: flex; + flex-direction: row; + align-items: center; } -.ActionableLayout .ActionableLayout__Left .ui.header.sub { - margin-bottom: 0; -} \ No newline at end of file +.ActionableLayout__Content { + height: calc(100% - 32px); +} diff --git a/src/components/Layout/ActionableLayout.tsx b/src/components/Layout/ActionableLayout.tsx index ed4a0aef6..15603308b 100644 --- a/src/components/Layout/ActionableLayout.tsx +++ b/src/components/Layout/ActionableLayout.tsx @@ -1,21 +1,14 @@ -import classNames from 'classnames' - import './ActionableLayout.css' -export type ActionableLayoutProps = React.HTMLAttributes & { +interface Props { leftAction?: React.ReactNode rightAction?: React.ReactNode + children: React.ReactNode } -export default function ActionableLayout({ - className, - rightAction, - leftAction, - children, - ...props -}: ActionableLayoutProps) { +export default function ActionableLayout({ rightAction, leftAction, children }: Props) { return ( -
+
{leftAction}
{rightAction}
diff --git a/src/components/Layout/BurgerMenu/BurgerMenu.css b/src/components/Layout/BurgerMenu/BurgerMenu.css deleted file mode 100644 index 1a3f2c811..000000000 --- a/src/components/Layout/BurgerMenu/BurgerMenu.css +++ /dev/null @@ -1,32 +0,0 @@ -.BurgerMenu { - width: 2rem; - height: 2rem; - display: flex; - justify-content: space-around; - flex-flow: column nowrap; - z-index: 10; -} - -.BurgerMenu .Bar { - width: 2rem; - height: 0.25rem; - border-radius: 10px; - background-color: black; - transform-origin: 1px; - transition: all 0.3s linear; -} - -.BurgerMenu .FilterBar { - width: 2rem; - height: 0.25rem; - border-radius: 10px; - background-color: black; - transform-origin: 1px; - transition: all 0.3s linear; -} - -@media (min-width: 768px) { - .BurgerMenu { - display: none; - } -} diff --git a/src/components/Layout/BurgerMenu/BurgerMenu.tsx b/src/components/Layout/BurgerMenu/BurgerMenu.tsx deleted file mode 100644 index 6f6d7f6d1..000000000 --- a/src/components/Layout/BurgerMenu/BurgerMenu.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useMemo } from 'react' - -import { useBurgerMenu } from '../../../hooks/useBurgerMenu' - -import './BurgerMenu.css' - -const FILTER_SHAPE_TRANSFORMS = [ - 'rotate(0)', - 'scale(.75, 0.8) translateX(0) translateX(15%)', - 'scale(0.15, 0.8) rotate(0) translateX(5rem)', -] -const CROSS_SHAPE_TRANSFORMS = ['rotate(45deg)', 'translateX(0) translateX(200%)', 'rotate(-45deg)'] -const BURGER_SHAPE_TRANSFORMS = ['rotate(0)', 'translateX(0)', 'rotate(0)'] - -function BurgerMenu() { - const { status, setStatus } = useBurgerMenu() - const { open, searching, filtering } = status - - const handleClick = () => { - if (!searching) { - setStatus((prevState) => ({ - ...prevState, - open: !prevState.open, - filtering: false, - })) - } else { - setStatus((prevState) => ({ - ...prevState, - filtering: !prevState.filtering, - })) - } - } - - const [bar1, bar2, bar3] = useMemo(() => { - if (searching && !filtering) return FILTER_SHAPE_TRANSFORMS - else if (open || filtering) return CROSS_SHAPE_TRANSFORMS - else return BURGER_SHAPE_TRANSFORMS - }, [open, searching, filtering]) - - return ( -
-
-
-
-
- ) -} - -export default BurgerMenu diff --git a/src/components/Layout/BurgerMenu/BurgerMenuContent.css b/src/components/Layout/BurgerMenu/BurgerMenuContent.css deleted file mode 100644 index ec0432479..000000000 --- a/src/components/Layout/BurgerMenu/BurgerMenuContent.css +++ /dev/null @@ -1,14 +0,0 @@ -.BurgerMenuContent.Animated { - position: absolute; - width: 100%; - top: 15px; -} - -.BurgerMenuContent--Padded { - top: 0; - padding: 0 16px; -} - -.BurgerMenuContent--PushedDown { - top: 65px !important; -} diff --git a/src/components/Layout/BurgerMenu/BurgerMenuContent.tsx b/src/components/Layout/BurgerMenu/BurgerMenuContent.tsx deleted file mode 100644 index 44cdb95ae..000000000 --- a/src/components/Layout/BurgerMenu/BurgerMenuContent.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import classNames from 'classnames' - -import { ProposalStatus, ProposalType } from '../../../entities/Proposal/types' -import { useBurgerMenu } from '../../../hooks/useBurgerMenu' -import CategoryList from '../../Category/CategoryList' -import CategoryFilter from '../../Search/CategoryFilter' -import SearchInputMobile from '../../Search/SearchInputMobile' -import StatusFilter from '../../Search/StatusFilter' -import TimeFrameFilter from '../../Search/TimeFrameFilter' -import { NavigationProps } from '../Navigation' - -import './BurgerMenuContent.css' -import MobileNavigation from './MobileNavigation' - -export type FilterStatus = { - categoryOpen: boolean - statusOpen: boolean - timeFrameOpen: boolean -} - -export type BurgerMenuContentProps = NavigationProps & { - navigationOnly?: boolean - snapshotStatusBarOpen?: boolean -} - -const filtersInitialStatus = { categoryOpen: true, statusOpen: false, timeFrameOpen: false } - -const OPEN_BURGER_HEIGHT = 895 -const SEARCH_TITLE_HEIGHT = 32 -const CATEGORY_FILTER_HEIGHT = 600 -const STATUS_FILTER_HEIGHT = 290 -const TIMEFRAME_FILTER_HEIGHT = 212 -const CLOSED_FILTER_HEIGHT = 56 -const MOBILE_NAVIGATION_HEIGHT = 180 - -function BurgerMenuContent({ navigationOnly, activeTab, snapshotStatusBarOpen }: BurgerMenuContentProps) { - const [footer, setFooter] = useState(null) - const [filterStatus, setFilterStatus] = useState(filtersInitialStatus) - const { status, setStatus } = useBurgerMenu() - const { open, searching, filtering, translate } = status - - useEffect(() => { - setFooter(document.querySelectorAll('.dcl.footer')[0]) - return () => { - if (footer) { - footer.setAttribute('style', '') - } - } - }, [footer]) - - useEffect(() => { - if (footer) { - footer.classList.add('Animated') - if (translate) { - footer.setAttribute('style', `transform: translateY(${translate})`) - } else { - footer.setAttribute('style', '') - } - } - }, [footer, translate]) - - function handleFilterStatusChange(status: FilterStatus) { - setFilterStatus(status) - } - - useEffect(() => { - if (!open || !filtering || !searching) { - setFilterStatus(filtersInitialStatus) - } - }, [open, filtering, searching]) - - useEffect(() => { - const filtersHeight = - (filterStatus.categoryOpen ? CATEGORY_FILTER_HEIGHT : CLOSED_FILTER_HEIGHT) + - (filterStatus.statusOpen ? STATUS_FILTER_HEIGHT : CLOSED_FILTER_HEIGHT) + - (filterStatus.timeFrameOpen ? TIMEFRAME_FILTER_HEIGHT : CLOSED_FILTER_HEIGHT) - - let newTranslate: string | undefined - if (navigationOnly && open) { - newTranslate = MOBILE_NAVIGATION_HEIGHT + SEARCH_TITLE_HEIGHT + 'px' - } else { - if (!open) { - newTranslate = undefined - } else { - newTranslate = (searching ? (filtering ? filtersHeight : SEARCH_TITLE_HEIGHT) : OPEN_BURGER_HEIGHT) + 'px' - } - } - setStatus((prev) => ({ ...prev, translate: newTranslate })) - }, [open, searching, filtering, filterStatus, navigationOnly, setStatus]) - - return ( -
- {navigationOnly ? ( - <> - - - - ) : ( - <> - - {searching && filtering && ( - <> - handleFilterStatusChange({ ...filterStatus, categoryOpen: open })} - filterType={ProposalType} - startOpen - /> - handleFilterStatusChange({ ...filterStatus, statusOpen: open })} - statusType={ProposalStatus} - /> - handleFilterStatusChange({ ...filterStatus, timeFrameOpen: open })} - /> - - )} - {!searching && ( - <> - - - - )} - - )} -
- ) -} - -export default React.memo(BurgerMenuContent) diff --git a/src/components/Layout/BurgerMenu/BurgerMenuLayout.tsx b/src/components/Layout/BurgerMenu/BurgerMenuLayout.tsx deleted file mode 100644 index f4d205917..000000000 --- a/src/components/Layout/BurgerMenu/BurgerMenuLayout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Mobile, NotMobile } from 'decentraland-ui/dist/components/Media/Media' - -import { useBurgerMenu } from '../../../hooks/useBurgerMenu' -import { NavigationProps } from '../Navigation' - -import BurgerMenuContent from './BurgerMenuContent' - -interface Props { - children: React.ReactNode - navigationOnly?: boolean - activeTab: NavigationProps['activeTab'] -} - -function BurgerMenuLayout({ children, navigationOnly, activeTab }: Props) { - const { status } = useBurgerMenu() - const { open, translate, snapshotStatusBarOpen } = status - return ( - <> - - -
- {children} -
-
- {children} - - ) -} - -export default BurgerMenuLayout diff --git a/src/components/Layout/Layout.css b/src/components/Layout/Layout.css index ce629b5f3..4ca6643f2 100644 --- a/src/components/Layout/Layout.css +++ b/src/components/Layout/Layout.css @@ -7,11 +7,6 @@ margin: 0 auto; } -.dcl.new-user-menu .toggle { - width: 42px !important; - height: 42px !important; -} - @media only screen and (min-width: 1200px) { .ui.container { width: 1124px; @@ -24,6 +19,11 @@ } @media (min-width: 768px) { + .dcl.new-user-menu .toggle { + width: 42px !important; + height: 42px !important; + } + .ui.container.WiderFooter { padding: 0 24px !important; } diff --git a/src/components/Layout/Navbar.tsx b/src/components/Layout/Navbar.tsx deleted file mode 100644 index 787822abf..000000000 --- a/src/components/Layout/Navbar.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { useLocation } from '@reach/router' -import UserInformation from 'decentraland-gatsby/dist/components/User/UserInformation' -import { Mobile, NotMobile } from 'decentraland-ui/dist/components/Media/Media' - -import BurgerMenu from './BurgerMenu/BurgerMenu' - -const BURGER_MENU_LOCATIONS = ['/', '/proposals/', '/transparency/', '/projects/', '/profile/'] - -function Navbar() { - const location = useLocation() - const showBurgerMenu = BURGER_MENU_LOCATIONS.some((burgerLocation) => burgerLocation === location.pathname) - - return ( - <> - {showBurgerMenu && ( - - - - )} - - - - - ) -} - -export default Navbar diff --git a/src/components/Layout/Navigation.css b/src/components/Layout/Navigation.css index 22a6128b4..aa33190fb 100644 --- a/src/components/Layout/Navigation.css +++ b/src/components/Layout/Navigation.css @@ -1,6 +1,6 @@ -.Navigation .dcl.tabs { - background: var(--background); - display: none; +.Navigation { + position: relative; + z-index: 9; } .Navigation .dcl.tab svg { @@ -14,38 +14,85 @@ border-radius: 6px; } -@media (min-width: 768px) { +.Navigation .dcl.tabs .ui.container { + display: flex; +} + +.Navigation .dcl.tabs a { + color: var(--text); +} + +.Navigation .dcl.tabs-left { + display: flex; + flex-direction: row; +} + +@media (max-width: 767px) { .Navigation .dcl.tabs { + background-color: var(--white-900); + padding-left: 4px; + padding-right: 36px; + margin-top: 0; + align-items: flex-end; + height: 65px; + } + + .Navigation .dcl.tabs-left { + width: 100%; + overflow: scroll; + } + + .Navigation .dcl.tab { + margin: 0 12px; + padding: 0; + padding-bottom: 14px; + white-space: nowrap; + } + + .Navigation .dcl.tabs .ui.container { display: flex; } - .Navigation .dcl.tabs a { - color: var(--text); + .Navigation .dcl.tab { + padding-bottom: 5px; + } + .Navigation .dcl.tab.active { + border-bottom: 3px solid var(--primary); + border-left: 0; + } + + /* Hide scrollbar for Chrome, Safari and Opera */ + .Navigation .dcl.tabs-left::-webkit-scrollbar { + display: none; + } + + /* Hide scrollbar for IE, Edge and Firefox */ + .Navigation .dcl.tabs-left { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ } +} +@media (min-width: 768px) { .Navigation .dcl.tabs .ui.container { max-width: 1440px !important; width: 100%; padding: 0 24px; } - - .dcl.tabs .ui.container { - display: flex; - } } -.dcl.tabs-right { +.Navigation .dcl.tabs .dcl.tabs-right { display: flex; align-items: center; } -.NavigationProfilePopUp { +.Navigation__ProfilePopUp { width: 152px; overflow-wrap: break-word; } -.NavigationProfilePopUp p, -.NavigationProfilePopUp .ui.button.basic { +.Navigation__ProfilePopUp p, +.Navigation__ProfilePopUp .ui.button.basic { font-style: normal; font-weight: var(--weight-normal); font-size: 13px; @@ -53,3 +100,9 @@ margin-bottom: 0; padding-left: 0; } + +.Navigation__SearchInput { + position: absolute; + top: 0; + right: 0; +} diff --git a/src/components/Layout/Navigation.tsx b/src/components/Layout/Navigation.tsx index 9c7f3af48..d513360ba 100644 --- a/src/components/Layout/Navigation.tsx +++ b/src/components/Layout/Navigation.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Button } from 'decentraland-ui/dist/components/Button/Button' -import { useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media' +import { Mobile, NotMobile, useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media' import { Popup } from 'decentraland-ui/dist/components/Popup/Popup' import { Tabs } from 'decentraland-ui/dist/components/Tabs/Tabs' @@ -13,6 +13,7 @@ import locations from '../../utils/locations' import Link from '../Common/Typography/Link' import Dot from '../Icon/Dot' import SearchInput from '../Search/SearchInput' +import SearchInputMobile from '../Search/SearchInputMobile' import './Navigation.css' @@ -80,7 +81,7 @@ const Navigation = ({ activeTab }: NavigationProps) => {

{t('navigation.profile_pop_up.label')}

@@ -115,10 +116,15 @@ const Navigation = ({ activeTab }: NavigationProps) => { )} - - - + + + + + + + +
) } diff --git a/src/components/Profile/ActivityBox.tsx b/src/components/Profile/ActivityBox.tsx index acc80efb9..279d2bf1e 100644 --- a/src/components/Profile/ActivityBox.tsx +++ b/src/components/Profile/ActivityBox.tsx @@ -1,7 +1,6 @@ import { useState } from 'react' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' -import { Container } from 'decentraland-ui/dist/components/Container/Container' import { CoauthorStatus } from '../../entities/Coauthor/types' import { isSameAddress } from '../../entities/Snapshot/utils' @@ -36,35 +35,31 @@ const ActivityBox = ({ address }: Props) => { const { requestsStatus } = useProposalsByCoAuthor(isLoggedUserProfile ? account : null, CoauthorStatus.PENDING) return ( - - - - - setActiveTab(Tab.MyProposals)} active={activeTab === Tab.MyProposals}> - {isLoggedUserProfile - ? t('page.profile.activity.my_proposals.title') - : t('page.profile.created_proposals.title')} + + + + setActiveTab(Tab.MyProposals)} active={activeTab === Tab.MyProposals}> + {isLoggedUserProfile + ? t('page.profile.activity.my_proposals.title') + : t('page.profile.created_proposals.title')} + + {isLoggedUserProfile && ( + setActiveTab(Tab.Watchlist)} active={activeTab === Tab.Watchlist}> + {t('page.profile.activity.watchlist.title')} - {isLoggedUserProfile && ( - setActiveTab(Tab.Watchlist)} active={activeTab === Tab.Watchlist}> - {t('page.profile.activity.watchlist.title')} - - )} - setActiveTab(Tab.CoAuthoring)} active={activeTab === Tab.CoAuthoring}> - {t('page.profile.activity.coauthoring.title')} - {requestsStatus.length > 0 && } - - - - - {activeTab === Tab.MyProposals && } - {activeTab === Tab.Watchlist && isLoggedUserProfile && } - {activeTab === Tab.CoAuthoring && ( - )} - - - + setActiveTab(Tab.CoAuthoring)} active={activeTab === Tab.CoAuthoring}> + {t('page.profile.activity.coauthoring.title')} + {requestsStatus.length > 0 && } + + + + + {activeTab === Tab.MyProposals && } + {activeTab === Tab.Watchlist && isLoggedUserProfile && } + {activeTab === Tab.CoAuthoring && } + + ) } diff --git a/src/components/Profile/GrantBeneficiaryBox.tsx b/src/components/Profile/GrantBeneficiaryBox.tsx index 3831c21af..3c45ac882 100644 --- a/src/components/Profile/GrantBeneficiaryBox.tsx +++ b/src/components/Profile/GrantBeneficiaryBox.tsx @@ -1,5 +1,3 @@ -import { Container } from 'decentraland-ui/dist/components/Container/Container' - import useFormatMessage from '../../hooks/useFormatMessage' import useGrantsByUser from '../../hooks/useGrantsByUser' @@ -18,10 +16,8 @@ export default function GrantBeneficiaryBox({ address }: Props) { if (!hasGrants) return null return ( - - - - - + + + ) } diff --git a/src/components/Profile/VotedProposalsBox.tsx b/src/components/Profile/VotedProposalsBox.tsx index 51bfb7178..f4c8a332a 100644 --- a/src/components/Profile/VotedProposalsBox.tsx +++ b/src/components/Profile/VotedProposalsBox.tsx @@ -1,5 +1,3 @@ -import { Container } from 'decentraland-ui/dist/components/Container/Container' - import useFormatMessage from '../../hooks/useFormatMessage' import useVotedProposals from '../../hooks/useVotedProposals' import Empty from '../Common/Empty' @@ -23,26 +21,24 @@ function VotedProposalsBox({ address }: Props) { const { votes, isLoading, handleViewMore, hasMoreProposals } = useVotedProposals(address, PROPOSALS_PER_PAGE) return ( - - - {isLoading && } - {!isLoading && - (votes.length > 0 ? ( - votes.map((vote) => { - return - }) - ) : ( - } - description={t('page.profile.voted_proposals.empty')} - /> - ))} - {hasMoreProposals && ( - {t('page.profile.voted_proposals.button')} - )} - - + + {isLoading && } + {!isLoading && + (votes.length > 0 ? ( + votes.map((vote) => { + return + }) + ) : ( + } + description={t('page.profile.voted_proposals.empty')} + /> + ))} + {hasMoreProposals && ( + {t('page.profile.voted_proposals.button')} + )} + ) } diff --git a/src/components/Profile/VpDelegationBox.tsx b/src/components/Profile/VpDelegationBox.tsx index c47b3584f..15c8be43b 100644 --- a/src/components/Profile/VpDelegationBox.tsx +++ b/src/components/Profile/VpDelegationBox.tsx @@ -2,7 +2,6 @@ import { useState } from 'react' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Button } from 'decentraland-ui/dist/components/Button/Button' -import { Container } from 'decentraland-ui/dist/components/Container/Container' import Grid from 'semantic-ui-react/dist/commonjs/collections/Grid/Grid' import { DelegationResult } from '../../clients/SnapshotTypes' @@ -38,7 +37,7 @@ function VpDelegationBox({ address, delegation, isLoadingDelegation, ownVp, isLo const profileHasADelegation = delegatedTo.length > 0 && !!ownVp return ( - +
)} - +
) } diff --git a/src/components/Profile/VpDelegatorsBox.tsx b/src/components/Profile/VpDelegatorsBox.tsx index 3caed5f8d..6bb4a5289 100644 --- a/src/components/Profile/VpDelegatorsBox.tsx +++ b/src/components/Profile/VpDelegatorsBox.tsx @@ -1,5 +1,3 @@ -import { Container } from 'decentraland-ui/dist/components/Container/Container' - import { DelegationResult, DetailedScores } from '../../clients/SnapshotTypes' import { isSameAddress } from '../../entities/Snapshot/utils' import useFormatMessage from '../../hooks/useFormatMessage' @@ -38,33 +36,31 @@ export default function VpDelegatorsBox({ !isLoggedUserProfile && !isLoadingUserVpDistribution && profileAddress && accountHasDelegations return ( - - - ) - } - > - {loggedUserVpDistribution && profileAddress && ( - - )} - - + ) + } + > + {loggedUserVpDistribution && profileAddress && ( + + )} + ) } diff --git a/src/components/Proposal/ProposalItem.css b/src/components/Proposal/ProposalItem.css index 80e3ee36b..f6487b98b 100644 --- a/src/components/Proposal/ProposalItem.css +++ b/src/components/Proposal/ProposalItem.css @@ -1,68 +1,48 @@ +.ProposalItem.ui.card { + margin: 0 !important; +} + .ProposalItem, .ui.card.Link.ProposalItem { width: 100% !important; border: 1px solid var(--black-100); } -.ProposalItem .ProposalItem__Title { +.ProposalItem__TitleContainer { + margin-bottom: 4px; +} + +.ProposalItem__Title { display: flex; flex-direction: row; padding-bottom: 0; - padding-right: 40px; } -.ProposalItem .ProposalItem__Title .ui.header { - flex: 1; - padding-right: 15px; -} - -.ProposalItem:visited .ProposalItem__Title .ui.header { +.ProposalItem:visited .ProposalItem__Title { color: var(--black-600); } -.ProposalItem .ProposalItem__Status { - padding-top: 8px; +.ProposalItem__Status { display: flex; flex-direction: row; justify-content: space-between; } @media (max-width: 425px) { - .ProposalItem .ProposalItem__Status { - flex-direction: row; - } - - .ProposalItem .ProposalItem__Title { - padding-right: 0; - } - - .ProposalItem .ProposalItem__Title .ui.header { - font-size: 17px; - } - .ActionableLayout .ActionableLayout__Content .ui.card > .content { padding: 12px; } } -.ProposalItem .ProposalItem__Title .ui.header:first-child { +.ProposalItem__Title .ui.header:first-child { margin-bottom: 0; } -.ProposalItem .ProposalItem__Title .ui.button { - position: absolute; - padding: 16px 20px; - top: 0; - right: 0; - display: none; -} - -.ProposalItem .ProposalItem__Title .ui.loading.loading.loading.loading.button { +.ProposalItem__Title .ui.loading.loading.loading.loading.button { position: absolute; } -.ProposalItem:hover .ProposalItem__Title .ui.button, -.ProposalItem.ProposalItem--subscribed .ProposalItem__Title .ui.button { +.ProposalItem:hover .ProposalItem__Title .ui.button { display: block; } @@ -109,3 +89,19 @@ .ProposalItem__Details > span.Username .dcl.blockie { margin: 0 0 0 0; } + +.ProposalItem__FinishLabel { + padding: 0; + display: inline-flex; + color: var(--black-800); + font-weight: var(--weight-semi-bold); + font-size: 10px; + line-height: 16px; + vertical-align: middle; + align-content: center; + align-items: center; + text-transform: uppercase; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} diff --git a/src/components/Proposal/ProposalItem.tsx b/src/components/Proposal/ProposalItem.tsx index 2b035993c..f18161ce1 100644 --- a/src/components/Proposal/ProposalItem.tsx +++ b/src/components/Proposal/ProposalItem.tsx @@ -1,79 +1,65 @@ import classNames from 'classnames' -import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' -import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Card } from 'decentraland-ui/dist/components/Card/Card' -import { Header } from 'decentraland-ui/dist/components/Header/Header' import { Desktop } from 'decentraland-ui/dist/components/Media/Media' import { ProposalAttributes } from '../../entities/Proposal/types' import { Vote } from '../../entities/Votes/types' +import useCountdown, { Countdown } from '../../hooks/useCountdown' import useFormatMessage from '../../hooks/useFormatMessage' -import useWinningChoice from '../../hooks/useWinningChoice' +import Time from '../../utils/date/Time' import locations from '../../utils/locations' import CategoryPill from '../Category/CategoryPill' +import DateTooltip from '../Common/DateTooltip' +import Heading from '../Common/Typography/Heading' import Link from '../Common/Typography/Link' import Username from '../Common/Username' -import Subscribe from '../Icon/Subscribe' -import Subscribed from '../Icon/Subscribed' import CoauthorRequestLabel from '../Status/CoauthorRequestLabel' -import FinishLabel from '../Status/FinishLabel' -import LeadingOption from '../Status/LeadingOption' import StatusPill from '../Status/StatusPill' import './ProposalItem.css' +function getTimeLabel(timeout: Countdown, date: Date, format?: 'short') { + const time = Time(date) + + return timeout.time > 0 ? time.fromNow() : format === 'short' ? time.format('MM/DD/YY') : time.format('MMM DD, YYYY') +} + interface Props { proposal: ProposalAttributes hasCoauthorRequest?: boolean votes?: Record - subscribed?: boolean - subscribing?: boolean - onSubscribe?: (e: React.MouseEvent, proposal: ProposalAttributes) => void } -export default function ProposalItem({ - proposal, - hasCoauthorRequest, - votes, - subscribing, - subscribed, - onSubscribe, -}: Props) { - const [account] = useAuthContext() - const { winningChoice, userChoice } = useWinningChoice(proposal) +export default function ProposalItem({ proposal, hasCoauthorRequest, votes }: Props) { const t = useFormatMessage() - - function handleSubscription(e: React.MouseEvent) { - e.stopPropagation() - e.preventDefault() - onSubscribe && onSubscribe(e, proposal) - } + const { id, title, status, type, user, start_at, finish_at } = proposal + const timeout = useCountdown(finish_at) + const isCountdownRunning = timeout.time > 0 + const hasStarted = Time().isAfter(start_at) + const endLabel = isCountdownRunning + ? `${t('page.proposal_list.finish_label.ends')} ` + : `${t('page.proposal_list.finish_label.ended')} ` + const label = hasStarted ? endLabel : `${t('page.proposal_list.finish_label.starts')} ` + const time = hasStarted ? finish_at : start_at return ( -
-
{proposal.title}
+
+ + {title} + {hasCoauthorRequest && } - {account && ( - - )}
- - - + + +
{votes && ( @@ -82,19 +68,11 @@ export default function ProposalItem({ )} - + + {`${label} ${getTimeLabel(timeout, time, 'short')}`} +
- {winningChoice.votes > 0 && ( - - = (proposal.required_to_pass || 0)} - userChoice={userChoice} - /> - - )}
diff --git a/src/components/Proposal/View/Budget/ContestedBudgetCard.css b/src/components/Proposal/View/Budget/ContestedBudgetCard.css index 9b977468c..18b735dce 100644 --- a/src/components/Proposal/View/Budget/ContestedBudgetCard.css +++ b/src/components/Proposal/View/Budget/ContestedBudgetCard.css @@ -6,9 +6,16 @@ .ContestedBudgetCard__Row { display: flex; + flex-direction: column; justify-content: space-between; } +@media (min-width: 768px) { + .ContestedBudgetCard__Row { + flex-direction: row; + } +} + .ContestedBudgetCard__Content { display: flex; flex-direction: column; diff --git a/src/components/Proposal/View/Budget/ProposalBudget.tsx b/src/components/Proposal/View/Budget/ProposalBudget.tsx index e9d591bdf..c8d10c240 100644 --- a/src/components/Proposal/View/Budget/ProposalBudget.tsx +++ b/src/components/Proposal/View/Budget/ProposalBudget.tsx @@ -26,13 +26,11 @@ export default function ProposalBudget({ proposal, budget }: Props) {
- - {contestantsAmount > 0 && isActive && ( -
- -
- )} -
+ {contestantsAmount > 0 && isActive && ( +
+ +
+ )} ) } diff --git a/src/components/Proposal/View/CompetingButton.css b/src/components/Proposal/View/CompetingButton.css index eccd0a703..e70c6d012 100644 --- a/src/components/Proposal/View/CompetingButton.css +++ b/src/components/Proposal/View/CompetingButton.css @@ -1,8 +1,7 @@ .CompetingButton { display: flex; width: 100%; - height: 47px; - padding: 11px 16px 12px 18px; + padding: 12px 16px; justify-content: space-between; align-items: center; background: var(--white-900); @@ -14,8 +13,20 @@ .CompetingButton__Title { font-weight: var(--weight-normal); - font-size: 13px; - line-height: 18px; + font-size: 12px; + line-height: 17px; text-transform: uppercase; color: var(--black-600); + text-align: left; +} + +@media (min-width: 768px) { + .CompetingButton { + height: 47px; + } + + .CompetingButton__Title { + font-size: 13px; + line-height: 18px; + } } diff --git a/src/components/Search/CategoryFilter.tsx b/src/components/Search/CategoryFilter.tsx index d0f0b7d9e..69e407744 100644 --- a/src/components/Search/CategoryFilter.tsx +++ b/src/components/Search/CategoryFilter.tsx @@ -8,7 +8,7 @@ import useURLSearchParams from '../../hooks/useURLSearchParams' import CategoryOption from '../Category/CategoryOption' import './CategoryFilter.css' -import CollapsibleFilter from './CollapsibleFilter' +import FilterContainer from './FilterContainer' export enum ProjectTypeFilter { All = 'all_projects', @@ -23,7 +23,6 @@ export type FilterType = | typeof ProjectTypeFilter export type Counter = Record export type FilterProps = { - onChange?: (open: boolean) => void startOpen?: boolean categoryCount?: Counter } @@ -32,8 +31,6 @@ const FILTER_KEY = 'type' export default function CategoryFilter({ filterType, - onChange, - startOpen, categoryCount, showAllFilter = true, }: FilterProps & { filterType: FilterType; showAllFilter?: boolean }) { @@ -43,7 +40,7 @@ export default function CategoryFilter({ const type = params.get(FILTER_KEY) return ( - + {showAllFilter && ( ) })} - + ) } diff --git a/src/components/Search/CollapsibleFilter.css b/src/components/Search/CollapsibleFilter.css deleted file mode 100644 index a5f24ab80..000000000 --- a/src/components/Search/CollapsibleFilter.css +++ /dev/null @@ -1,80 +0,0 @@ -.CollapsibleFilter { - position: relative; - min-height: 32px; - margin-bottom: 8px; - margin-top: 16px; -} - -.CollapsibleFilter:first-child { - margin-top: 8px; -} - -.FilterHeader { - display: flex; - justify-content: space-between; - margin-top: 8px; - margin-bottom: 8px; - cursor: pointer; -} - -.FilterHeader__Title { - text-transform: uppercase; - margin-bottom: 0; -} - -@media (max-width: 767px) { - .FilterHeader { - justify-content: unset; - } - - .PlusMinusContainer { - border-radius: 50%; - background-color: #e1e0e3; - margin-left: 8px; - } - - .PlusMinus { - left: 18% !important; - top: 43% !important; - } -} - -.FilterHeader:first-child { - margin-top: 0; -} - -.FilterContent { - opacity: 0; - height: 0; - overflow: hidden; - transition: opacity 1s ease-out; -} - -.FilterContent--open { - height: auto; - opacity: 1; -} - -.PlusMinusContainer { - width: 1rem; - height: 1rem; - border: 0; - position: relative; - display: flex; -} - -.PlusMinus { - position: absolute; - width: 10px; - border-radius: 10px; - background-color: var(--secondary-text); - transition: all 0.3s linear; - bottom: 25%; - left: 50%; - height: 10%; - top: 45%; -} - -.PlusMinus--closed { - transform: rotate(90deg); -} diff --git a/src/components/Search/CollapsibleFilter.tsx b/src/components/Search/CollapsibleFilter.tsx deleted file mode 100644 index d4fd78615..000000000 --- a/src/components/Search/CollapsibleFilter.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useState } from 'react' - -import classNames from 'classnames' - -import Text from '../Common/Typography/Text' - -import './CollapsibleFilter.css' - -type Props = { - title: string - startOpen?: boolean - children: React.ReactNode - onChange?: (open: boolean) => void -} - -function CollapsibleFilter({ title, children, startOpen, onChange }: Props) { - const [open, setOpen] = useState(!!startOpen) - - useEffect(() => { - onChange && onChange(open) - }, [onChange, open]) - - const toggleHandler = () => { - setOpen(!open) - onChange && onChange(!open) - } - - return ( -
-
- - {title} - -
-
-
-
-
-
{children}
-
- ) -} - -export default CollapsibleFilter diff --git a/src/components/Search/FilterContainer.css b/src/components/Search/FilterContainer.css new file mode 100644 index 000000000..970ff900e --- /dev/null +++ b/src/components/Search/FilterContainer.css @@ -0,0 +1,30 @@ +.FilterContainer { + position: relative; + min-height: 32px; + margin-bottom: 8px; + margin-top: 16px; +} + +.FilterContainer:first-child { + margin-top: 8px; +} + +.FilterContainer__TitleContainer { + display: flex; + margin-bottom: 8px; +} + +.FilterContainer__TitleContainer:first-child { + margin-top: 0; +} + +.FilterContainer__Title { + text-transform: uppercase; + margin-bottom: 0; +} + +.FilterContainer__Content { + height: auto; + overflow: hidden; + transition: opacity 1s ease-out; +} diff --git a/src/components/Search/FilterContainer.tsx b/src/components/Search/FilterContainer.tsx new file mode 100644 index 000000000..6e28e3273 --- /dev/null +++ b/src/components/Search/FilterContainer.tsx @@ -0,0 +1,21 @@ +import Text from '../Common/Typography/Text' + +import './FilterContainer.css' + +type Props = { + title: string + children: React.ReactNode +} + +export default function FilterContainer({ title, children }: Props) { + return ( +
+
+ + {title} + +
+
{children}
+
+ ) +} diff --git a/src/components/Search/FilterLabel.tsx b/src/components/Search/FilterLabel.tsx index 7c0bec26f..c97300826 100644 --- a/src/components/Search/FilterLabel.tsx +++ b/src/components/Search/FilterLabel.tsx @@ -1,41 +1,24 @@ import classNames from 'classnames' -import { navigate } from '../../utils/locations' +import Link from '../Common/Typography/Link' import Text from '../Common/Typography/Text' import './FilterLabel.css' -export type FilterLabelProps = Omit, 'children'> & { +interface Props { active?: boolean label: string + href: string } -export default function FilterLabel({ active, label, className, ...props }: FilterLabelProps) { - function handleClick(e: React.MouseEvent) { - if (props.onClick) { - props.onClick(e) - } - - if (!e.defaultPrevented) { - e.preventDefault() - - if (props.href) { - navigate(props.href) - } - } - } - +export default function FilterLabel({ active, label, href }: Props) { return ( - + - + {label} - + ) } diff --git a/src/components/Search/FilterMenu.css b/src/components/Search/FilterMenu.css new file mode 100644 index 000000000..f62c2ab04 --- /dev/null +++ b/src/components/Search/FilterMenu.css @@ -0,0 +1,61 @@ +.FilterMenu__Button { + cursor: pointer; + display: flex; + align-items: center; + height: 40px; + gap: 4px; +} + +.FilterMenu__ButtonText { + margin-bottom: 0; + font-size: 12px !important; + font-weight: var(--weight-semi-bold) !important; + text-transform: uppercase; +} + +@media (min-width: 768px) { + .FilterMenu__ButtonText { + font-size: 14px !important; + } +} + +.FilterMenu__Sidebar { + position: fixed; + top: 0; + right: 0; + z-index: 11; + height: 100%; + width: 293px; + overflow-y: scroll; + overscroll-behavior: none; + visibility: hidden; + transform: translateX(293px); + transition: all 0.5s ease; + box-shadow: 0px 0px 35px 0px var(--alpha-black-400); +} + +.FilterMenu__Sidebar::-webkit-scrollbar { + display: none; +} + +.FilterMenu__Sidebar--open { + transform: translateX(0px); + visibility: visible; +} + +.FilterMenu__SidebarContent { + position: absolute; + top: 0; + right: 0; + width: 293px; + min-height: 100%; + background-color: var(--white-900); + z-index: 11; + padding: 15px 32px; +} + +.FilterMenu__CloseButton { + position: absolute; + top: 23px; + right: 23px; +} diff --git a/src/components/Search/FilterMenu.tsx b/src/components/Search/FilterMenu.tsx new file mode 100644 index 000000000..976f2bbb0 --- /dev/null +++ b/src/components/Search/FilterMenu.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react' + +import classNames from 'classnames' + +import useFormatMessage from '../../hooks/useFormatMessage' +import Text from '../Common/Typography/Text' +import Cross from '../Icon/Cross' +import Filter from '../Icon/Filter' +import Overlay from '../Sidebar/Overlay' + +import './FilterMenu.css' + +interface Props { + children: React.ReactNode +} + +export default function FilterMenu({ children }: Props) { + const t = useFormatMessage() + const [isOpen, setIsOpen] = useState(false) + const handleClose = () => setIsOpen(false) + + return ( +
+
setIsOpen((prev) => !prev)}> + + {t('navigation.search.filter')} + + +
+ +
+
+ {children} +
+ +
+
+
+
+ ) +} diff --git a/src/components/Search/SearchInput.tsx b/src/components/Search/SearchInput.tsx index 365acb367..94eef4345 100644 --- a/src/components/Search/SearchInput.tsx +++ b/src/components/Search/SearchInput.tsx @@ -10,7 +10,7 @@ import locations, { navigate } from '../../utils/locations' import './SearchInput.css' -export function handleSearch(textSearch: string, location: Location) { +function handleSearch(textSearch: string, location: Location) { const newParams = new URLSearchParams(location.search) if (textSearch) { newParams.set('search', textSearch) @@ -24,7 +24,7 @@ export function handleSearch(textSearch: string, location: Location) { navigate(locations.proposals(newParams)) } -export default function SearchInput(props: React.HTMLAttributes) { +export default function SearchInput() { const t = useFormatMessage() const location = useLocation() const { search } = useProposalsSearchParams() @@ -69,10 +69,9 @@ export default function SearchInput(props: React.HTMLAttributes .dcl.close { - position: absolute; +.SearchInputMobile--open { + width: 100%; + display: flex; + height: 65px; + padding-left: 10px; + padding-right: 16px; top: 0; - right: 16px; - margin: 8px; - background-color: #f1f6fe; z-index: 10; } -.SearchInputMobile { +.SearchInputMobile__Gradient { + width: 16px; + height: 34.5px; + position: absolute; + left: -16px; + bottom: 0; + background: linear-gradient(270deg, var(--white-900) 0%, rgba(255, 255, 255, 0) 103.13%); +} + +.SearchInputMobile__Input { + width: 16px; + padding: 8px 13px; border: 0; - padding: 8px 32px; + background: var(--white-900) no-repeat 8px center; + background-image: url('../../images/icons/new-magnify.svg'); + outline: none; +} + +.SearchInputMobile__Input--open { width: 100%; - border-radius: 24px; - background: #f2f3f5 url('../../images/icons/magnify.svg') no-repeat 25% center; - float: right; - outline-width: 0; - transition: all 0.1s ease-in-out; + padding: 8px 32px; } -@media (max-width: 374px) { - .SearchInputMobile { - background: #f2f3f5 url('../../images/icons/magnify.svg') no-repeat 20% center; - } +.SearchInputMobile__CloseButton { + display: flex; + align-items: center; + padding: 0; + border: none; + background: none; } -.SearchInputMobile:focus { - box-shadow: var(--shadow-2); +.SearchInputMobile__Container { + display: flex; + flex-direction: row; + position: relative; + width: 100%; } -input.SearchInputMobile::placeholder { - text-align: center; - color: var(--secondary-text); - transition: all 0.2s ease-in-out 0.15s; +.SearchInputMobile__Overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: var(--alpha-black-300); + z-index: 10; + visibility: hidden; } -.SearchInputMobile--open { - background: #f2f3f5 url('../../images/icons/magnify.svg') no-repeat 8px center; - transition: all 0.15s ease-in-out 0s; +.SearchInputMobile__Overlay--with-snapshot-status { + top: 163px; } -.SearchInputMobile.SearchInputMobile--open:focus { - outline: none; +.SearchInputMobile__Overlay--open { + visibility: visible; +} + +.Navigation .dcl.tabs, +.dcl.navbar { + z-index: 10 !important; } -input.SearchInputMobile--open::placeholder { - text-align: left; - transition: all 0.2s ease-in-out 0.15s; +/* Hide tabs if search input mobile is open to fix position bug */ +.Navigation .dcl.tabs:has(+ .SearchInputMobile--open) { + visibility: hidden; } diff --git a/src/components/Search/SearchInputMobile.tsx b/src/components/Search/SearchInputMobile.tsx index 230ba0ffd..fc360f459 100644 --- a/src/components/Search/SearchInputMobile.tsx +++ b/src/components/Search/SearchInputMobile.tsx @@ -1,81 +1,113 @@ -import { useEffect, useRef, useState } from 'react' +import { useCallback, useLayoutEffect, useRef, useState } from 'react' import { useLocation } from '@reach/router' import classNames from 'classnames' -import { Close } from 'decentraland-ui/dist/components/Close/Close' -import { useBurgerMenu } from '../../hooks/useBurgerMenu' import useFormatMessage from '../../hooks/useFormatMessage' import { useProposalsSearchParams } from '../../hooks/useProposalsSearchParams' +import locations, { navigate } from '../../utils/locations' +import Cross from '../Icon/Cross' -import { handleSearch } from './SearchInput' import './SearchInputMobile.css' -export default function SearchInputMobile(props: React.HTMLAttributes) { +export default function SearchInputMobile() { const t = useFormatMessage() const location = useLocation() - const { search, searching } = useProposalsSearchParams() + const { search } = useProposalsSearchParams() const searchInput = useRef(null) const [open, setOpen] = useState(false) - const [searchText, setSearchText] = useState(() => search || '') - const [placeholder, setPlaceholder] = useState(t('navigation.search.mobile.placeholder') || '') - const burgerMenu = useBurgerMenu() - useEffect(() => { - burgerMenu?.setStatus((prev) => ({ ...prev, searching: searching })) - }, [searching]) + const [searchText, setSearchText] = useState(search || '') - function focusSearch() { - searchInput.current?.focus() - } - - useEffect(() => { - if (!search) { - setSearchText('') + const handleSearch = (textSearch: string, location: Location) => { + const newParams = new URLSearchParams(location.search) + if (textSearch) { + newParams.set('search', textSearch) + newParams.delete('page') + newParams.delete('order') } else { - setSearchText(search) - setOpen(true) + newParams.delete('search') + newParams.delete('page') } - }, [search]) - function handleChange(e: React.ChangeEvent) { + handleClose() + searchInput.current?.blur() + navigate(locations.proposals(newParams)) + } + + const handleChange = (e: React.ChangeEvent) => { setSearchText(e.target.value) } - function handleClear() { - setSearchText('') - focusSearch() + const handleClear = () => { + if (searchText === '') { + handleClose() + } else { + setSearchText('') + searchInput.current?.focus() + searchInput.current?.click() + } } - function handleKeyPress(e: React.KeyboardEvent) { + const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleSearch(searchText, location) } } - function handleFocus() { - setOpen(true) - if (!searchText) setPlaceholder(t('navigation.search.mobile.focus_placeholder') || '') + const handleKeyUp = (e: React.KeyboardEvent) => { + if (e.code === 'Escape') { + setSearchText('') + } } - function handleBlur() { - setOpen(!!searchText) - if (!searchText) setPlaceholder(t('navigation.search.mobile.placeholder') || '') - } + const handleClose = useCallback(() => { + searchInput.current?.blur() + setOpen(false) + }, []) + + const lastScroll = useRef(0) + useLayoutEffect(() => { + if (typeof window !== 'undefined') { + const onScroll = () => { + const isScrollDown = window.scrollY > lastScroll.current + lastScroll.current = window.scrollY + if (isScrollDown) { + handleClose() + } + } + + window.addEventListener('scroll', onScroll) + + return () => window.removeEventListener('scroll', onScroll) + } + }, [handleClose]) return ( -
- +
- {searchText && open && } -
+
+
+ {!open &&
} + setOpen(true)} + /> + {open && ( + + )} +
+
+ ) } diff --git a/src/components/Search/SearchTitle.css b/src/components/Search/SearchTitle.css index 70d05831c..7197c7d64 100644 --- a/src/components/Search/SearchTitle.css +++ b/src/components/Search/SearchTitle.css @@ -1,77 +1,53 @@ -.SearchTitle { - padding-top: 16px; - padding-bottom: 16px; +.SearchTitle__Container { + display: flex; + align-items: center; } -.SearchTitle .SearchTitle__Container { - display: flex; - align-items: center; +.SearchTitle__TextContainer { + display: contents; } -.SearchTitle .SearchTitle_TextContainer { - display: contents; +.SearchTitle__BackContainer { + width: 32px; + height: 32px; + margin-right: 12px; } -.SearchTitle .dcl.back { - background-position-x: 9px; - background-position-y: 8px; -} - -.BigBack { - min-width: 32px; - min-height: 32px; -} - -.SmallBack { - min-width: 26px; - min-height: 26px; +.SearchTitle__Back { + margin-left: 0 !important; } .ui.header.SearchTitle__Text { - display: block; - margin: 0; - padding-left: 8px; + display: block; + margin: 0; + font-size: 17px; } .ui.header.SearchTitle__Text.SearchTitle__ClosingDoubleQuote { - padding-left: 0; + padding-left: 0; } -.ui.header.SearchTitle__Ellipsis{ - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; +.ui.header.SearchTitle__Ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .SearchTitle__Separator { - width: 100%; - border-color: #e1e0e3; - opacity: 0.2; - margin-top: 16px; -} - -@media only screen and (min-width: 768px) and (max-width: 991px) { - .ui.header.SearchTitle__Text { - padding-left: 8px; - } + width: 100%; + border-color: #e1e0e3; + opacity: 0.2; + margin-top: 16px; } @media (min-width: 768px) { - .SearchTitle { - padding-bottom: 1rem; - padding-top: 0; - } - - .SearchTitle .dcl.back { - top: 1.2em; - left: -4em; - background-position-x: 10px; - background-position-y: 9px; - } - - .ui.header.SearchTitle__Text { - padding-left: 24px; - font-size: 34px; - line-height: 42px; - } + .SearchTitle__Container { + padding-bottom: 1rem; + padding-top: 0; + } + + .ui.header.SearchTitle__Text { + font-size: 34px; + line-height: 42px; + } } diff --git a/src/components/Search/SearchTitle.tsx b/src/components/Search/SearchTitle.tsx index bf966dd64..895754d7b 100644 --- a/src/components/Search/SearchTitle.tsx +++ b/src/components/Search/SearchTitle.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames' import { Back } from 'decentraland-ui/dist/components/Back/Back' import { Header } from 'decentraland-ui/dist/components/Header/Header' -import { useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media' +import { NotMobile } from 'decentraland-ui/dist/components/Media/Media' import useFormatMessage from '../../hooks/useFormatMessage' import { useProposalsSearchParams } from '../../hooks/useProposalsSearchParams' @@ -9,29 +9,27 @@ import locations, { navigate } from '../../utils/locations' import './SearchTitle.css' -export function SearchTitle() { +export default function SearchTitle() { const t = useFormatMessage() const { search } = useProposalsSearchParams() - const isMobile = useMobileMediaQuery() + + if (!search) { + return null + } return ( - <> - {search && isMobile &&
} - {search && ( -
-
-
- navigate(locations.proposals())} /> -
-
-
- {t('navigation.search.search_results', { title: search })} -
-
{'"'}
-
-
+
+ +
+ navigate(locations.proposals())} />
- )} - +
+
+
+ {t('navigation.search.search_results', { title: search })} +
+
{'"'}
+
+
) } diff --git a/src/components/Search/SortingMenu.css b/src/components/Search/SortingMenu.css index 232d35be7..d1abdb6b5 100644 --- a/src/components/Search/SortingMenu.css +++ b/src/components/Search/SortingMenu.css @@ -1,11 +1,31 @@ .SortingMenu { - margin-right: 1em; + margin-right: 1em; + font-size: 12px !important; + height: 40px; + display: flex !important; + justify-content: center; + align-items: center; } -.ui.dropdown.SortingMenu.Upwards>.dropdown.icon:before { - content: '\F0D8'; +.SortingMenu .divider.text { + font-weight: var(--weight-semi-bold) !important; + line-height: 24px !important; } -.ui.dropdown.SortingMenu.Downwards>.dropdown.icon:before { - content: '\F0D7'; +@media (min-width: 768px) { + .SortingMenu { + font-size: 14px !important; + } +} + +.ui.dropdown.SortingMenu .dropdown.icon { + margin-left: 7px; +} + +.ui.dropdown.SortingMenu.Upwards > .dropdown.icon:before { + content: '\F0D8'; +} + +.ui.dropdown.SortingMenu.Downwards > .dropdown.icon:before { + content: '\F0D7'; } diff --git a/src/components/Search/SortingMenu.tsx b/src/components/Search/SortingMenu.tsx index e3fc871b5..f964267a8 100644 --- a/src/components/Search/SortingMenu.tsx +++ b/src/components/Search/SortingMenu.tsx @@ -17,7 +17,7 @@ export default function SortingMenu() { const location = useLocation() const params = useMemo(() => new URLSearchParams(location.search), [location.search]) const order = useMemo(() => (params.get('order') === 'ASC' ? 'ASC' : 'DESC'), [params]) - const arrowDirection = useMemo(() => (order === 'ASC' ? 'Downwards' : 'Upwards'), [order]) + const arrowDirection = useMemo(() => (order === 'ASC' ? 'Upwards' : 'Downwards'), [order]) const isMobile = useMobileMediaQuery() const t = useFormatMessage() diff --git a/src/components/Search/StatusFilter.tsx b/src/components/Search/StatusFilter.tsx index aa597bcdc..fa5bc0937 100644 --- a/src/components/Search/StatusFilter.tsx +++ b/src/components/Search/StatusFilter.tsx @@ -10,14 +10,14 @@ import { getUrlFilters } from '../../helpers' import useFormatMessage from '../../hooks/useFormatMessage' import { FilterProps } from './CategoryFilter' -import CollapsibleFilter from './CollapsibleFilter' +import FilterContainer from './FilterContainer' import FilterLabel from './FilterLabel' type StatusType = typeof ProposalStatus | typeof ProjectStatus const FILTER_KEY = 'status' -export default function StatusFilter({ onChange, startOpen, statusType }: FilterProps & { statusType: StatusType }) { +export default function StatusFilter({ statusType }: FilterProps & { statusType: StatusType }) { const t = useFormatMessage() const location = useLocation() const params = useMemo(() => new URLSearchParams(location.search), [location.search]) @@ -25,7 +25,7 @@ export default function StatusFilter({ onChange, startOpen, statusType }: Filter const isGrantFilter = isEqual(statusType, ProjectStatus) return ( - + {Object.values(statusType).map((value, index) => { const label = toSnakeCase(value) @@ -40,6 +40,6 @@ export default function StatusFilter({ onChange, startOpen, statusType }: Filter ) } })} - + ) } diff --git a/src/components/Search/TimeFrameFilter.tsx b/src/components/Search/TimeFrameFilter.tsx index ce225b943..17154d597 100644 --- a/src/components/Search/TimeFrameFilter.tsx +++ b/src/components/Search/TimeFrameFilter.tsx @@ -4,11 +4,10 @@ import useFormatMessage from '../../hooks/useFormatMessage' import useURLSearchParams from '../../hooks/useURLSearchParams' import locations from '../../utils/locations' -import { FilterProps } from './CategoryFilter' -import CollapsibleFilter from './CollapsibleFilter' +import FilterContainer from './FilterContainer' import FilterLabel from './FilterLabel' -export default function TimeFrameFilter({ onChange }: FilterProps) { +export default function TimeFrameFilter() { const t = useFormatMessage() const params = useURLSearchParams() const timeFrame = useMemo(() => params.get('timeFrame') || null, [params]) @@ -21,11 +20,7 @@ export default function TimeFrameFilter({ onChange }: FilterProps) { } return ( - + - + ) } diff --git a/src/components/Sidebar/GovernanceSidebar.css b/src/components/Sidebar/GovernanceSidebar.css index c0bf40c60..06be918a4 100644 --- a/src/components/Sidebar/GovernanceSidebar.css +++ b/src/components/Sidebar/GovernanceSidebar.css @@ -1,20 +1,36 @@ -.ui.push.right.very.wide.visible.sidebar.GovernanceSidebar { +.GovernanceSidebar { + display: flex; + flex-direction: column; + gap: 32px; width: 100%; + background: var(--white-900); + padding: 26px; + position: fixed; + top: 0; + right: 0; + z-index: 11; + height: 100%; + overflow-y: scroll; + overscroll-behavior: none; + visibility: hidden; + transform: translateX(650px); + transition: all 0.5s ease; + box-shadow: 0px 0px 35px 0px var(--alpha-black-400); } @media (min-width: 768px) { - .ui.push.right.very.wide.visible.sidebar.GovernanceSidebar { + .GovernanceSidebar { width: 650px; + overflow-y: auto; + overscroll-behavior: contain; } } -.GovernanceSidebar { - display: flex; - flex-direction: column; - gap: 32px; - background: var(--white-900); - padding-top: 26px; - padding-left: 27px; - padding-right: 24px; - cursor: default; +.GovernanceSidebar::-webkit-scrollbar { + display: none; +} + +.GovernanceSidebar--open { + transform: translateX(0px); + visibility: visible; } diff --git a/src/components/Sidebar/GovernanceSidebar.tsx b/src/components/Sidebar/GovernanceSidebar.tsx index 2ae74a799..ea8c05743 100644 --- a/src/components/Sidebar/GovernanceSidebar.tsx +++ b/src/components/Sidebar/GovernanceSidebar.tsx @@ -1,50 +1,39 @@ -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' -import Sidebar from 'semantic-ui-react/dist/commonjs/modules/Sidebar/Sidebar' +import classNames from 'classnames' import './GovernanceSidebar.css' +import Overlay from './Overlay' type Props = { - visible?: boolean + visible: boolean children: React.ReactNode onShow?: () => void onHide?: () => void - onClose?: () => void + onClose: () => void } -export default function GovernanceSidebar({ visible, onShow, onHide, onClose, children }: Props) { +const ANIMATION_DURATION = 500 + +export default function GovernanceSidebar({ visible = false, onShow, onHide, onClose, children }: Props) { + const prevVisible = useRef(visible) useEffect(() => { - function handleClickOutside(event: MouseEvent) { - const sidebar = document.querySelector('.GovernanceSidebar') - if (sidebar && !sidebar.contains(event.target as Node) && !!onClose) { - event.preventDefault() - event.stopPropagation() - onClose() + if (prevVisible.current && !visible) { + if (onHide) { + onHide() + } + } else if (!prevVisible.current && visible) { + if (onShow) { + setTimeout(onShow, ANIMATION_DURATION) } } - - if (visible) { - document.addEventListener('mousedown', handleClickOutside) - } else { - document.removeEventListener('mousedown', handleClickOutside) - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside) - } - }, [visible, onClose]) + prevVisible.current = visible + }, [onHide, onShow, visible]) return ( - - {children} - + <> + +
{children}
+ ) } diff --git a/src/components/Sidebar/Overlay.css b/src/components/Sidebar/Overlay.css new file mode 100644 index 000000000..af743e39a --- /dev/null +++ b/src/components/Sidebar/Overlay.css @@ -0,0 +1,19 @@ +.Overlay { + width: 100%; + height: 100%; + z-index: 10; + background-color: var(--alpha-black-300); + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + visibility: hidden; + transition: all 0.5s ease; +} + +.Overlay--open { + visibility: visible; + opacity: 1; +} diff --git a/src/components/Sidebar/Overlay.tsx b/src/components/Sidebar/Overlay.tsx new file mode 100644 index 000000000..55374d027 --- /dev/null +++ b/src/components/Sidebar/Overlay.tsx @@ -0,0 +1,12 @@ +import classNames from 'classnames' + +import './Overlay.css' + +interface Props { + isOpen: boolean + onClick: () => void +} + +export default function Overlay({ isOpen, onClick }: Props) { + return
+} diff --git a/src/components/Status/FinishLabel.css b/src/components/Status/FinishLabel.css deleted file mode 100644 index 6cf0dbba1..000000000 --- a/src/components/Status/FinishLabel.css +++ /dev/null @@ -1,16 +0,0 @@ -.FinishLabel { - padding: 0; - height: 24px; - display: inline-flex; - color: var(--black-800); - font-weight: var(--weight-semi-bold); - font-size: 12px; - line-height: 26px; - vertical-align: middle; - align-content: center; - align-items: center; - text-transform: uppercase; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} diff --git a/src/components/Status/FinishLabel.tsx b/src/components/Status/FinishLabel.tsx deleted file mode 100644 index 74374ae5c..000000000 --- a/src/components/Status/FinishLabel.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Mobile, NotMobile } from 'decentraland-ui/dist/components/Media/Media' - -import useCountdown, { Countdown } from '../../hooks/useCountdown' -import useFormatMessage from '../../hooks/useFormatMessage' -import Time from '../../utils/date/Time' -import DateTooltip from '../Common/DateTooltip' - -import './FinishLabel.css' - -interface Props { - startAt: Date - finishAt: Date -} - -function getTimeLabel(timeout: Countdown, date: Date, format?: 'short') { - const time = Time(date) - - return timeout.time > 0 ? time.fromNow() : format === 'short' ? time.format('MM/DD/YY') : time.format('MMM DD, YYYY') -} - -export default function FinishLabel({ startAt, finishAt }: Props) { - const timeout = useCountdown(finishAt) - const t = useFormatMessage() - const isCountdownRunning = timeout.time > 0 - const hasStarted = Time().isAfter(startAt) - const endLabel = isCountdownRunning - ? `${t('page.proposal_list.finish_label.ends')} ` - : `${t('page.proposal_list.finish_label.ended')} ` - const label = hasStarted ? endLabel : `${t('page.proposal_list.finish_label.starts')} ` - const time = hasStarted ? finishAt : startAt - - return ( - - - {`${label} ${getTimeLabel(timeout, time, 'short')}`} - - - {`${label} ${getTimeLabel(timeout, time)}`} - - - ) -} diff --git a/src/components/User/Badges/BadgeCard.css b/src/components/User/Badges/BadgeCard.css index 7bc0e2029..216e369db 100644 --- a/src/components/User/Badges/BadgeCard.css +++ b/src/components/User/Badges/BadgeCard.css @@ -1,5 +1,4 @@ .BadgeCard { - max-width: 185px; height: 206px; background: var(--white-900); border: 1px solid var(--alpha-black-300); @@ -14,6 +13,12 @@ cursor: pointer; } +@media (min-width: 768px) { + .BadgeCard { + max-width: 185px; + } +} + .BadgeCard:hover { box-shadow: 0 0 8px var(--black-600); } @@ -42,7 +47,12 @@ overflow: hidden; text-overflow: ellipsis; margin-bottom: -4px; - max-width: 160px; +} + +@media (min-width: 768px) { + .BadgeCard__Title { + max-width: 160px; + } } .BadgeCard__MintDate { diff --git a/src/components/User/Badges/Badges.tsx b/src/components/User/Badges/Badges.tsx index f903fb351..98daa38d6 100644 --- a/src/components/User/Badges/Badges.tsx +++ b/src/components/User/Badges/Badges.tsx @@ -28,7 +28,6 @@ export default function Badges({ address }: Props) { const handleSidebarClose = useCallback(() => { setSidebarOpen(false) - setBadgeInDetail(null) }, []) useEffect(() => { diff --git a/src/components/User/Badges/BadgesSidebar.tsx b/src/components/User/Badges/BadgesSidebar.tsx index 0efa68e25..884670aa8 100644 --- a/src/components/User/Badges/BadgesSidebar.tsx +++ b/src/components/User/Badges/BadgesSidebar.tsx @@ -21,20 +21,13 @@ export default function BadgesSidebar({ isSidebarVisible, onClose, badges, badge const t = useFormatMessage() const { currentBadges, expiredBadges } = badges - const handleClose = (e: React.MouseEvent) => { - e.preventDefault() - e.stopPropagation() - setBadgeInDetail(null) - onClose() - } - return ( - + {!badgeInDetail && (
{t('page.profile.badges_sidebar.title')} - +
@@ -68,7 +61,7 @@ export default function BadgesSidebar({ isSidebarVisible, onClose, badges, badge {t('page.profile.badges_sidebar.detail_title')} - +
diff --git a/src/components/User/UserStats.css b/src/components/User/UserStats.css index d9ad46a6f..5b377c0b2 100644 --- a/src/components/User/UserStats.css +++ b/src/components/User/UserStats.css @@ -1,4 +1,4 @@ -.ui.container.UserStats__Container { +.UserStats { display: flex; gap: 41px; } diff --git a/src/components/User/UserStats.tsx b/src/components/User/UserStats.tsx index 9a45d1baf..aaaac1287 100644 --- a/src/components/User/UserStats.tsx +++ b/src/components/User/UserStats.tsx @@ -1,7 +1,6 @@ import { Suspense, lazy } from 'react' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' -import { Container } from 'decentraland-ui/dist/components/Container/Container' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' import { NotMobile, useMobileMediaQuery } from 'decentraland-ui/dist/components/Media/Media' @@ -38,7 +37,7 @@ export default function UserStats({ address, vpDistribution, isLoadingVpDistribu const { total } = vpDistribution || { total: 0 } return ( - +
@@ -63,6 +62,6 @@ export default function UserStats({ address, vpDistribution, isLoadingVpDistribu - +
) } diff --git a/src/hooks/useBurgerMenu.ts b/src/hooks/useBurgerMenu.ts deleted file mode 100644 index adb0bffa5..000000000 --- a/src/hooks/useBurgerMenu.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useContext } from 'react' - -import { BurgerMenuStatusContext, BurgerMenuStatusContextType } from '../components/Context/BurgerMenuStatusContext' - -export function useBurgerMenu() { - const context = useContext(BurgerMenuStatusContext) - if (context === undefined) { - throw new Error('useBurgerMenu must be used within BurgerMenuStatusContextProvider') - } - - return context -} diff --git a/src/images/icons/new-magnify.svg b/src/images/icons/new-magnify.svg new file mode 100644 index 000000000..30b426fcf --- /dev/null +++ b/src/images/icons/new-magnify.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/intl/en.json b/src/intl/en.json index 73422ae7f..6e6467bd4 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -37,6 +37,7 @@ "placeholder": "Search...", "search_results": "Search results for \"{title}", "no_matches": "No proposals match the search criteria", + "filter": "Filter", "sorting": { "DESC": "Latest", "ASC": "Oldest" diff --git a/src/pages/index.tsx b/src/pages/index.tsx index e07d6a248..4a7306afa 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -9,7 +9,6 @@ import DaoDelegates from '../components/Home/DaoDelegates' import MainBanner from '../components/Home/MainBanner' import MetricsCards from '../components/Home/MetricsCards' import OpenProposals from '../components/Home/OpenProposals' -import BurgerMenuLayout from '../components/Layout/BurgerMenu/BurgerMenuLayout' import LoadingView from '../components/Layout/LoadingView' import MaintenanceLayout from '../components/Layout/MaintenanceLayout' import Navigation, { NavigationTab } from '../components/Layout/Navigation' @@ -51,25 +50,20 @@ export default function HomePage() { {!endingSoonProposals && } {endingSoonProposals && ( - - - - - {isLoadingProposals && } - {!isLoadingProposals && ( - <> - - - - - - - )} - - + + + + {isLoadingProposals && } + {!isLoadingProposals && ( + <> + + + + + + + )} + )} ) diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 259a2036f..3de5be407 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -6,7 +6,6 @@ import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext import isEthereumAddress from 'validator/lib/isEthereumAddress' import WiderContainer from '../components/Common/WiderContainer' -import BurgerMenuLayout from '../components/Layout/BurgerMenu/BurgerMenuLayout' import LoadingView from '../components/Layout/LoadingView' import LogIn from '../components/Layout/LogIn' import MaintenanceLayout from '../components/Layout/MaintenanceLayout' @@ -61,7 +60,7 @@ export default function ProfilePage() { } return ( - + <> - + ) } diff --git a/src/pages/projects.tsx b/src/pages/projects.tsx index 8c22dff0d..04765304e 100644 --- a/src/pages/projects.tsx +++ b/src/pages/projects.tsx @@ -5,7 +5,6 @@ import { NotMobile } from 'decentraland-ui/dist/components/Media/Media' import toSnakeCase from 'lodash/snakeCase' import WiderContainer from '../components/Common/WiderContainer' -import BurgerMenuLayout from '../components/Layout/BurgerMenu/BurgerMenuLayout' import LoadingView from '../components/Layout/LoadingView' import MaintenanceLayout from '../components/Layout/MaintenanceLayout' import Navigation, { NavigationTab } from '../components/Layout/Navigation' @@ -116,22 +115,17 @@ export default function ProjectsPage() { {isLoadingProjects && } {!isLoadingProjects && ( - - - -
-
- - - - - -
+ + +
+
+ + + + + +
+
{displayableProjects && ( )}
- - +
+
)} ) diff --git a/src/pages/proposals.css b/src/pages/proposals.css index 805e3a1e8..401f6360a 100644 --- a/src/pages/proposals.css +++ b/src/pages/proposals.css @@ -21,6 +21,9 @@ } .ProposalsPage__List { + display: flex; + flex-direction: column; + gap: 16px; margin-bottom: 16px; } @@ -52,7 +55,22 @@ position: relative; } -.Animated { - width: 100%; - transition: all 0.5s ease 0s; +.ProposalsPage__ProposalCount { + font-size: 12px !important; + text-transform: uppercase; + color: var(--black-600); +} + +@media (min-width: 768px) { + .ProposalsPage__ProposalCount { + font-size: 14px !important; + } +} + +.ProposalsPage__LoaderContainer { + display: flex; + align-items: center; + justify-content: center; + position: relative; + height: 250px; } diff --git a/src/pages/proposals.tsx b/src/pages/proposals.tsx index 4b0473da4..2fff970c3 100644 --- a/src/pages/proposals.tsx +++ b/src/pages/proposals.tsx @@ -4,13 +4,8 @@ import { useLocation } from '@reach/router' import Head from 'decentraland-gatsby/dist/components/Head/Head' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Button } from 'decentraland-ui/dist/components/Button/Button' -import { Header } from 'decentraland-ui/dist/components/Header/Header' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' -import { - NotMobile, - useMobileMediaQuery, - useTabletAndBelowMediaQuery, -} from 'decentraland-ui/dist/components/Media/Media' +import { Mobile, NotMobile, useTabletAndBelowMediaQuery } from 'decentraland-ui/dist/components/Media/Media' import { Pagination } from 'decentraland-ui/dist/components/Pagination/Pagination' import RandomBanner from '../components/Banner/RandomBanner' @@ -18,27 +13,25 @@ import CategoryBanner from '../components/Category/CategoryBanner' import Empty from '../components/Common/Empty' import ProposalPreviewCard from '../components/Common/ProposalPreviewCard/ProposalPreviewCard' import Link from '../components/Common/Typography/Link' +import Text from '../components/Common/Typography/Text' import WiderContainer from '../components/Common/WiderContainer' import ActionableLayout from '../components/Layout/ActionableLayout' -import BurgerMenuLayout from '../components/Layout/BurgerMenu/BurgerMenuLayout' -import LoadingView from '../components/Layout/LoadingView' import MaintenanceLayout from '../components/Layout/MaintenanceLayout' import Navigation, { NavigationTab } from '../components/Layout/Navigation' import ProposalItem from '../components/Proposal/ProposalItem' import CategoryFilter from '../components/Search/CategoryFilter' -import { SearchTitle } from '../components/Search/SearchTitle' +import FilterMenu from '../components/Search/FilterMenu' +import SearchTitle from '../components/Search/SearchTitle' import SortingMenu from '../components/Search/SortingMenu' import StatusFilter from '../components/Search/StatusFilter' import TimeFrameFilter from '../components/Search/TimeFrameFilter' import { CoauthorStatus } from '../entities/Coauthor/types' import { ProposalStatus, ProposalType } from '../entities/Proposal/types' -import { useBurgerMenu } from '../hooks/useBurgerMenu' import useFormatMessage from '../hooks/useFormatMessage' import useProposals from '../hooks/useProposals' import useProposalsByCoAuthor from '../hooks/useProposalsByCoAuthor' import { useProposalsSearchParams } from '../hooks/useProposalsSearchParams' import useProposalsVotes from '../hooks/useProposalsVotes' -import useSubscriptions from '../hooks/useSubscriptions' import locations, { navigate } from '../utils/locations' import { isUnderMaintenance } from '../utils/maintenance' @@ -62,11 +55,7 @@ export default function ProposalsPage() { }) const proposalIds = (proposals?.data || []).map((proposal) => proposal.id) const { votes, isLoadingVotes } = useProposalsVotes(proposalIds) - const [subscriptions, subscriptionsState] = useSubscriptions() - const isMobile = useMobileMediaQuery() const isTabletAndBelow = useTabletAndBelowMediaQuery() - const { status: burgerStatus } = useBurgerMenu() - const { open } = burgerStatus const handlePageFilter = useCallback( (page: number) => { @@ -89,128 +78,121 @@ export default function ProposalsPage() { const [user] = useAuthContext() const { requestsStatus } = useProposalsByCoAuthor(user, CoauthorStatus.PENDING) + const title = + (type === ProposalType.Catalyst && t('page.proposal_catalyst_list.title')) || + (type === ProposalType.POI && t('page.proposal_poi_list.title')) || + (type === ProposalType.BanName && t('page.proposal_ban_name_list.title')) || + (type === ProposalType.Poll && t('page.proposal_poll_list.title')) || + t('page.proposal_list.title') || + '' + + const description = + (type === ProposalType.Catalyst && t('page.proposal_catalyst_list.description')) || + (type === ProposalType.POI && t('page.proposal_poi_list.description')) || + (type === ProposalType.BanName && t('page.proposal_ban_name_list.description')) || + (type === ProposalType.Poll && t('page.proposal_poll_list.description')) || + t('page.proposal_list.description') || + '' + if (isUnderMaintenance()) { - return ( - - ) + return } - const isLoading = !proposals || (isLoadingProposals && isLoadingVotes) + const isLoading = isLoadingProposals || isLoadingVotes return ( <> -
+ -
- + + - {isLoading && } - {!isLoading && ( - -
- -
- {!isMobile && search && proposals && } -
- -
-
- - - -
+ +
+ +
+ {search && proposals && ( + + + + )} +
+ +
+
+ + +
- - -
- {isMobile && proposals && } - - {!proposals && ''} - {proposals && t('general.count_proposals', { count: proposals.total || 0 })} - - } - rightAction={ - !searching && ( - <> - {proposals && } - - - ) - } - > -
- - {type && !searching && } - {proposals && proposals.data.length === 0 && ( - 0 - ? t('navigation.search.no_matches') - : t('page.proposal_list.no_proposals_yet') - } - /> - )} - {proposals && - proposals.data.map((proposal) => { +
+ +
+ {proposals && ( + + + + )} + + {t('general.count_proposals', { count: proposals.total || 0 })} + + ) + } + rightAction={ + <> + {proposals && ( + <> + + + + + + + + + + )} + + + } + > + {type && !searching && } + {isLoading && ( +
+ +
+ )} + {!isLoading && ( + <> + {proposals && proposals.data.length === 0 && ( + 0 + ? t('navigation.search.no_matches') + : t('page.proposal_list.no_proposals_yet') + } + /> + )} + {proposals && ( +
+ {proposals.data.map((proposal) => { return isTabletAndBelow ? ( req.proposal_id === proposal.id)} votes={votes ? votes[proposal.id] : undefined} - subscribing={subscriptionsState.isSubscribing} - subscribed={ - !!subscriptions.find((subscription) => subscription.proposal_id === proposal.id) - } - onSubscribe={(_, proposal) => subscriptionsState.subscribe(proposal.id)} /> ) : ( ) })} -
+
+ )} {proposals && proposals.total > ITEMS_PER_PAGE && ( handlePageFilter(activePage as number)} @@ -231,12 +214,12 @@ export default function ProposalsPage() { lastItem={null} /> )} -
-
-
+ + )} +
- - )} +
+
) } diff --git a/src/pages/transparency.tsx b/src/pages/transparency.tsx index 06b9b5738..0b4349512 100644 --- a/src/pages/transparency.tsx +++ b/src/pages/transparency.tsx @@ -13,7 +13,6 @@ import Document from '../components/Icon/Document' import DocumentOutline from '../components/Icon/DocumentOutline' import OpenFolder from '../components/Icon/OpenFolder' import Person from '../components/Icon/Person' -import BurgerMenuLayout from '../components/Layout/BurgerMenu/BurgerMenuLayout' import LoadingView from '../components/Layout/LoadingView' import Navigation, { NavigationTab } from '../components/Layout/Navigation' import SidebarLinkButton from '../components/Proposal/View/SidebarLinkButton' @@ -53,138 +52,136 @@ export default function TransparencyPage() {
{!data && } {data && ( - - -
-
- - {t('page.transparency.mission.title')} - -

{t('page.transparency.mission.description')}

- }> - {t('page.transparency.mission.join_discord_button')} - - }> - {t('page.transparency.mission.docs_button')} - - }> - {t('page.transparency.mission.dashboard_button')} - - }> - {t('page.transparency.mission.data_source_button')} - -
-
-
- - - - {t('page.transparency.mission.balance_title')} - -
- {balances && - balances.map((tokenBalance, index) => { - return ( - - ) - })} -
-
-
-
-
- - -
-
+ +
+
+ + {t('page.transparency.mission.title')} + +

{t('page.transparency.mission.description')}

+ }> + {t('page.transparency.mission.join_discord_button')} + + }> + {t('page.transparency.mission.docs_button')} + + }> + {t('page.transparency.mission.dashboard_button')} + + }> + {t('page.transparency.mission.data_source_button')} +
- -
-
- - {t('page.transparency.funding.title')} - -

{t('page.transparency.funding.description')}

- } isExternal={false}> - {t('page.transparency.funding.view_all_button')} - -
- -
+
+
- - {t('page.transparency.funding.total_title')} + + {t('page.transparency.mission.balance_title')} -
-
-
- {'$' + formatBalance(data.funding.total)} -
USD
-
-
+
+ {balances && + balances.map((tokenBalance, index) => { + return ( + + ) + })}
- -
+
+ + +
+
-
-
-
- - {t('page.transparency.members.title')} +
+
+ + {t('page.transparency.funding.title')} + +

{t('page.transparency.funding.description')}

+ } isExternal={false}> + {t('page.transparency.funding.view_all_button')} + +
+ +
+ + + + {t('page.transparency.funding.total_title')} -

{t('page.transparency.members.description')}

+
+
+
+ {'$' + formatBalance(data.funding.total)} +
USD
+
+
+
+
+ + +
+
+
- }> - {t('page.transparency.members.about_dao_button')} - - }> - {t('page.transparency.members.wearables_curator_button')} - - }> - {t('page.transparency.members.delegate_button')} - -
-
+
+
+
+ + {t('page.transparency.members.title')} + +

{t('page.transparency.members.description')}

-
- - {data && - data.committees.map((team, index) => { - return ( - - ) - })} - + }> + {t('page.transparency.members.about_dao_button')} + + }> + {t('page.transparency.members.wearables_curator_button')} + + }> + {t('page.transparency.members.delegate_button')} +
- - + +
+ + {data && + data.committees.map((team, index) => { + return ( + + ) + })} + +
+
+ )}
diff --git a/src/ui-overrides.css b/src/ui-overrides.css index 9aa9adea1..ba728c53f 100644 --- a/src/ui-overrides.css +++ b/src/ui-overrides.css @@ -226,3 +226,7 @@ margin: 0 !important; padding: 0 !important; } + +.dcl.login-modal .dcl.option { + width: 100% !important; +}