diff --git a/web/package.json b/web/package.json index 7fb6d1826..ea2cc5bb4 100644 --- a/web/package.json +++ b/web/package.json @@ -82,6 +82,8 @@ "graphql": "^16.7.1", "graphql-request": "~6.1.0", "moment": "^2.29.4", + "overlayscrollbars": "^2.3.0", + "overlayscrollbars-react": "^0.5.2", "react": "^18.2.0", "react-chartjs-2": "^4.3.1", "react-dom": "^18.2.0", diff --git a/web/src/app.tsx b/web/src/app.tsx index f6a705ac6..594489272 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -6,6 +6,7 @@ import "react-toastify/dist/ReactToastify.css"; import Web3Provider from "context/Web3Provider"; import QueryClientProvider from "context/QueryClientProvider"; import StyledComponentsProvider from "context/StyledComponentsProvider"; +import { FilterProvider } from "context/FilterProvider"; import RefetchOnBlock from "context/RefetchOnBlock"; import Layout from "layout/index"; import Home from "./pages/Home"; @@ -20,16 +21,18 @@ const App: React.FC = () => { - - }> - } /> - } /> - } /> - } /> - } /> - Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯} /> - - + + + }> + } /> + } /> + } /> + } /> + } /> + Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯} /> + + + diff --git a/web/src/assets/svgs/header/header-darkmode-desktop.svg b/web/src/assets/svgs/hero/hero-darkmode-desktop.svg similarity index 99% rename from web/src/assets/svgs/header/header-darkmode-desktop.svg rename to web/src/assets/svgs/hero/hero-darkmode-desktop.svg index 53ac43cab..348292627 100644 --- a/web/src/assets/svgs/header/header-darkmode-desktop.svg +++ b/web/src/assets/svgs/hero/hero-darkmode-desktop.svg @@ -1,4 +1,4 @@ - + @@ -71,3 +71,4 @@ + diff --git a/web/src/assets/svgs/header/header-darkmode-mobile.svg b/web/src/assets/svgs/hero/hero-darkmode-mobile.svg similarity index 99% rename from web/src/assets/svgs/header/header-darkmode-mobile.svg rename to web/src/assets/svgs/hero/hero-darkmode-mobile.svg index 5e2f9c3df..19e028aa6 100644 --- a/web/src/assets/svgs/header/header-darkmode-mobile.svg +++ b/web/src/assets/svgs/hero/hero-darkmode-mobile.svg @@ -1,4 +1,4 @@ - + diff --git a/web/src/assets/svgs/header/header-lightmode-desktop.svg b/web/src/assets/svgs/hero/hero-lightmode-desktop.svg similarity index 99% rename from web/src/assets/svgs/header/header-lightmode-desktop.svg rename to web/src/assets/svgs/hero/hero-lightmode-desktop.svg index c654a4710..ffd6e9135 100644 --- a/web/src/assets/svgs/header/header-lightmode-desktop.svg +++ b/web/src/assets/svgs/hero/hero-lightmode-desktop.svg @@ -1,4 +1,4 @@ - + diff --git a/web/src/assets/svgs/header/header-lightmode-mobile.svg b/web/src/assets/svgs/hero/hero-lightmode-mobile.svg similarity index 99% rename from web/src/assets/svgs/header/header-lightmode-mobile.svg rename to web/src/assets/svgs/hero/hero-lightmode-mobile.svg index a50f09da1..f0170be39 100644 --- a/web/src/assets/svgs/header/header-lightmode-mobile.svg +++ b/web/src/assets/svgs/hero/hero-lightmode-mobile.svg @@ -1,4 +1,4 @@ - + diff --git a/web/src/assets/svgs/icons/grid.svg b/web/src/assets/svgs/icons/grid.svg new file mode 100644 index 000000000..eb3fa4e05 --- /dev/null +++ b/web/src/assets/svgs/icons/grid.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/src/assets/svgs/icons/list.svg b/web/src/assets/svgs/icons/list.svg new file mode 100644 index 000000000..338c5b4a4 --- /dev/null +++ b/web/src/assets/svgs/icons/list.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/src/assets/svgs/icons/vea.svg b/web/src/assets/svgs/icons/vea.svg new file mode 100644 index 000000000..1fa6470d0 --- /dev/null +++ b/web/src/assets/svgs/icons/vea.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/src/assets/svgs/socialmedia/twitter.svg b/web/src/assets/svgs/socialmedia/twitter.svg deleted file mode 100644 index 458fd4f9b..000000000 --- a/web/src/assets/svgs/socialmedia/twitter.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/web/src/assets/svgs/socialmedia/x.svg b/web/src/assets/svgs/socialmedia/x.svg new file mode 100644 index 000000000..e9faf4183 --- /dev/null +++ b/web/src/assets/svgs/socialmedia/x.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index b9e9c9415..85f4dbf41 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -1,13 +1,38 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { StandardPagination } from "@kleros/ui-components-library"; +import { landscapeStyle } from "styles/landscapeStyle"; +import { useFiltersContext } from "context/FilterProvider"; import { CasesPageQuery } from "queries/useCasesQuery"; import DisputeCard from "components/DisputeCard"; +import CasesListHeader from "./CasesListHeader"; +import { useLocation } from "react-router-dom"; -const Container = styled.div` +const GridContainer = styled.div<{ path: string }>` display: flex; flex-wrap: wrap; justify-content: center; + align-items: center; + gap: 8px; + ${({ path }) => + landscapeStyle(() => + path === "/dashboard" + ? css` + display: flex; + ` + : css` + display: grid; + row-gap: 16px; + column-gap: 8px; + grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); + justify-content: space-between; + ` + )} +`; +const ListContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; gap: 8px; `; @@ -26,13 +51,26 @@ export interface ICasesGrid { } const CasesGrid: React.FC = ({ disputes, currentPage, setCurrentPage, numberDisputes, casesPerPage }) => { + const { isList } = useFiltersContext(); + const location = useLocation(); + + const path = location.pathname; return ( <> - - {disputes.map((dispute, i) => { - return ; - })} - + {!isList ? ( + + {disputes.map((dispute) => { + return ; + })} + + ) : ( + + {isList && } + {disputes.map((dispute) => { + return ; + })} + + )} theme.secondaryText} !important; + } + + ${landscapeStyle( + () => + css` + display: flex; + ` + )} +`; + +const StyledLabel = styled.label` + padding-left: calc(4px + (8 - 4) * ((100vw - 300px) / (900 - 300))); +`; + +const tooltipMsg = + "Users have an economic interest in serving as jurors in Kleros: " + + "collecting the Juror Rewards in exchange for their work. Each juror who " + + "is coherent with the final ruling receive the Juror Rewards composed of " + + "arbitration fees (ETH) + PNK redistribution between jurors."; + +const CasesListHeader: React.FC = () => { + return ( + + + # + Title + + + Court + Category + + Rewards: + + Next Deadline + + + ); +}; + +export default CasesListHeader; diff --git a/web/src/components/CasesDisplay/Filters.tsx b/web/src/components/CasesDisplay/Filters.tsx index d74c06574..26f706793 100644 --- a/web/src/components/CasesDisplay/Filters.tsx +++ b/web/src/components/CasesDisplay/Filters.tsx @@ -1,6 +1,11 @@ import React from "react"; -import styled, { useTheme } from "styled-components"; +import styled, { useTheme, css } from "styled-components"; +import { useWindowSize } from "react-use"; import { DropdownSelect } from "@kleros/ui-components-library"; +import { useFiltersContext } from "context/FilterProvider"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; +import ListIcon from "svgs/icons/list.svg"; +import GridIcon from "svgs/icons/grid.svg"; const Container = styled.div` display: flex; @@ -9,8 +14,44 @@ const Container = styled.div` width: fit-content; `; +const glowingEffect = css` + filter: drop-shadow(0 0 4px ${({ theme }) => theme.klerosUIComponentsSecondaryPurple}); +`; + +const StyledGridIcon = styled(GridIcon)<{ isList: boolean }>` + cursor: pointer; + transition: filter 0.2s ease; + fill: ${({ theme }) => theme.primaryBlue}; + width: 16px; + height: 16px; + overflow: hidden; + ${({ isList }) => !isList && glowingEffect} +`; + +const IconsContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 4px; +`; + +const StyledListIcon = styled(ListIcon)<{ isList: boolean; isScreenBig: boolean }>` + cursor: pointer; + display: ${({ isScreenBig }) => (isScreenBig ? "block" : "none")}; + transition: filter 0.2s ease; + fill: ${({ theme }) => theme.primaryBlue}; + width: 16px; + height: 16px; + overflow: hidden; + ${({ isList }) => isList && glowingEffect} +`; + const Filters: React.FC = () => { const theme = useTheme(); + const { width } = useWindowSize(); + const { isList, setIsList } = useFiltersContext(); + const screenIsBig = width > BREAKPOINT_LANDSCAPE; + return ( { defaultValue={0} callback={() => {}} /> + + setIsList(false)} /> + { + if (screenIsBig) { + setIsList(true); + } + }} + /> + ); }; diff --git a/web/src/components/ConnectWallet/AccountDisplay.tsx b/web/src/components/ConnectWallet/AccountDisplay.tsx index d64cdca5d..d39e7c60e 100644 --- a/web/src/components/ConnectWallet/AccountDisplay.tsx +++ b/web/src/components/ConnectWallet/AccountDisplay.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { useAccount, useNetwork, useEnsAvatar, useEnsName } from "wagmi"; import Identicon from "react-identicons"; import { shortenAddress } from "utils/shortenAddress"; @@ -7,8 +8,24 @@ import { shortenAddress } from "utils/shortenAddress"; const Container = styled.div` display: flex; flex-direction: column; + justify-content: space-between; + height: auto; align-items: flex-start; gap: 8px; + align-items: center; + background-color: ${({ theme }) => theme.whiteBackground}; + padding: 0px; + + ${landscapeStyle( + () => css` + background-color: ${({ theme }) => theme.whiteLowOpacity}; + flex-direction: row; + align-content: center; + border-radius: 300px; + gap: 0px; + padding: 0 12px; + ` + )} `; const AccountContainer = styled.div` @@ -17,22 +34,36 @@ const AccountContainer = styled.div` align-items: center; width: fit-content; gap: 8px; + > label { - color: ${({ theme }) => theme.primaryText}; font-size: 16px; font-weight: 600; } + + ${landscapeStyle( + () => css` + gap: 12px; + > label { + color: ${({ theme }) => theme.primaryText}; + font-weight: 400; + font-size: 14px; + } + ` + )} `; const ChainConnectionContainer = styled.div` + display: flex; width: fit-content; min-height: 32px; - display: flex; align-items: center; + padding-left: 0px; > label { color: ${({ theme }) => theme.success}; font-size: 16px; - margin-right: 4px; + + font-weight: 500; } + :before { content: ""; width: 8px; @@ -41,6 +72,12 @@ const ChainConnectionContainer = styled.div` border-radius: 50%; background-color: ${({ theme }) => theme.success}; } + + ${landscapeStyle( + () => css` + display: none; + ` + )} `; const StyledIdenticon = styled(Identicon)<{ size: `${number}` }>` diff --git a/web/src/components/ConnectWallet/index.tsx b/web/src/components/ConnectWallet/index.tsx index 59c070d32..f1c30a007 100644 --- a/web/src/components/ConnectWallet/index.tsx +++ b/web/src/components/ConnectWallet/index.tsx @@ -1,19 +1,9 @@ import React from "react"; -import styled from "styled-components"; import { useAccount, useNetwork, useSwitchNetwork } from "wagmi"; import { useWeb3Modal } from "@web3modal/react"; import { Button } from "@kleros/ui-components-library"; import { SUPPORTED_CHAINS, DEFAULT_CHAIN } from "consts/chains"; import AccountDisplay from "./AccountDisplay"; -import { DisconnectWalletButton } from "layout/Header/navbar/Menu/Settings/General"; - -const Container = styled.div` - display: flex; - gap: 16px; - justify-content: space-between; - flex-wrap: wrap; - align-items: center; -`; export const SwitchChainButton: React.FC = () => { const { switchNetwork, isLoading } = useSwitchNetwork(); @@ -49,13 +39,7 @@ const ConnectWallet: React.FC = () => { if (isConnected) { if (chain && chain.id !== DEFAULT_CHAIN) { return ; - } else - return ( - - - - - ); + } else return ; } else return ; }; diff --git a/web/src/components/DisputeCard/DisputeInfo.tsx b/web/src/components/DisputeCard/DisputeInfo.tsx index dc36b524f..742db46aa 100644 --- a/web/src/components/DisputeCard/DisputeInfo.tsx +++ b/web/src/components/DisputeCard/DisputeInfo.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { useFiltersContext } from "context/FilterProvider"; import { Periods } from "consts/periods"; import BookmarkIcon from "svgs/icons/bookmark.svg"; import CalendarIcon from "svgs/icons/calendar.svg"; @@ -8,10 +9,20 @@ import PileCoinsIcon from "svgs/icons/pile-coins.svg"; import RoundIcon from "svgs/icons/round.svg"; import Field from "../Field"; -const Container = styled.div` +const Container = styled.div<{ isList: boolean }>` display: flex; - flex-direction: column; + flex-direction: ${({ isList }) => (isList ? "row" : "column")}; gap: 8px; + + ${({ isList }) => + isList && + css` + gap: calc(4px + (24px - 4px) * ((100vw - 300px) / (900 - 300))); + `}; + justify-content: ${({ isList }) => (isList ? "space-around" : "center")}; + align-items: center; + width: 100%; + height: 100%; `; const getPeriodPhrase = (period: Periods): string => { @@ -37,15 +48,29 @@ export interface IDisputeInfo { round?: number; } +const formatDate = (date: number) => { + const options: Intl.DateTimeFormatOptions = { year: "numeric", month: "long", day: "numeric" }; + const startingDate = new Date(date * 1000); + const formattedDate = startingDate.toLocaleDateString("en-US", options); + return formattedDate; +}; + const DisputeInfo: React.FC = ({ courtId, court, category, rewards, period, date, round }) => { + const { isList } = useFiltersContext(); + return ( - + {court && courtId && } {category && } + {!category && isList && } {round && } {rewards && } {typeof period !== "undefined" && date && ( - + )} ); diff --git a/web/src/components/DisputeCard/PeriodBanner.tsx b/web/src/components/DisputeCard/PeriodBanner.tsx index fb8dd3e0a..0ad1ac647 100644 --- a/web/src/components/DisputeCard/PeriodBanner.tsx +++ b/web/src/components/DisputeCard/PeriodBanner.tsx @@ -3,7 +3,7 @@ import styled, { Theme } from "styled-components"; import { Periods } from "consts/periods"; const Container = styled.div>` - height: 45px; + height: ${({ isCard }) => (isCard ? "45px" : "100%")}; width: auto; border-top-right-radius: 3px; border-top-left-radius: 3px; @@ -21,11 +21,11 @@ const Container = styled.div>` margin-right: 8px; } } - ${({ theme, period }) => { + ${({ theme, period, isCard }) => { const [frontColor, backgroundColor] = getPeriodColors(period, theme); return ` - border-top: 5px solid ${frontColor}; - background-color: ${backgroundColor}; + ${isCard ? `border-top: 5px solid ${frontColor}` : `border-left: 5px solid ${frontColor}`}; + ${isCard && `background-color: ${backgroundColor}`}; .front-color { color: ${frontColor}; } @@ -41,6 +41,7 @@ const Container = styled.div>` export interface IPeriodBanner { id: number; period: Periods; + isCard?: boolean; } const getPeriodColors = (period: Periods, theme: Theme): [string, string] => { @@ -65,9 +66,9 @@ const getPeriodLabel = (period: Periods): string => { } }; -const PeriodBanner: React.FC = ({ id, period }) => ( - - {getPeriodLabel(period)} +const PeriodBanner: React.FC = ({ id, period, isCard = true }) => ( + + {isCard && {getPeriodLabel(period)}} #{id} ); diff --git a/web/src/components/DisputeCard/index.tsx b/web/src/components/DisputeCard/index.tsx index 6a27b0277..9fea91054 100644 --- a/web/src/components/DisputeCard/index.tsx +++ b/web/src/components/DisputeCard/index.tsx @@ -1,10 +1,12 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { useNavigate } from "react-router-dom"; import { formatEther } from "viem"; import { StyledSkeleton } from "components/StyledSkeleton"; import { Card } from "@kleros/ui-components-library"; import { Periods } from "consts/periods"; +import { useFiltersContext } from "context/FilterProvider"; +import { landscapeStyle } from "styles/landscapeStyle"; import { CasesPageQuery } from "queries/useCasesQuery"; import { useCourtPolicy } from "queries/useCourtPolicy"; import { useDisputeTemplate } from "queries/useDisputeTemplate"; @@ -14,13 +16,23 @@ import { isUndefined } from "utils/index"; import { useVotingHistory } from "hooks/queries/useVotingHistory"; const StyledCard = styled(Card)` - max-width: 380px; - min-width: 312px; - width: auto; + width: 312px; height: 260px; + ${landscapeStyle( + () => + css` + width: 380px; + ` + )} +`; +const StyledListItem = styled(Card)` + display: flex; + flex-grow: 1; + width: 100%; + height: 64px; `; -const Container = styled.div` +const CardContainer = styled.div` height: 215px; padding: 24px; display: flex; @@ -30,6 +42,25 @@ const Container = styled.div` margin: 0; } `; +const ListContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: flex-start; + width: 100%; + margin-right: 8px; + + h3 { + margin: 0; + } +`; + +const ListTitle = styled.div` + display: flex; + height: 100%; + justify-content: start; + align-items: center; + width: calc(30vw + (40 - 30) * ((100vw - 300px) / (1250 - 300))); +`; export const getPeriodEndTimestamp = ( lastPeriodChange: string, @@ -40,6 +71,11 @@ export const getPeriodEndTimestamp = ( return parseInt(lastPeriodChange) + durationCurrentPeriod; }; +const TruncatedTitle = ({ text, maxLength }) => { + const truncatedText = text.length <= maxLength ? text : text.slice(0, maxLength) + "…"; + return {truncatedText}; +}; + const DisputeCard: React.FC = ({ id, arbitrated, @@ -47,6 +83,7 @@ const DisputeCard: React.FC = ({ lastPeriodChange, court, }) => { + const { isList } = useFiltersContext(); const currentPeriodIndex = Periods[period]; const rewards = `≥ ${formatEther(court.feeForJuror)} ETH`; const date = @@ -66,19 +103,41 @@ const DisputeCard: React.FC = ({ const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds; const navigate = useNavigate(); return ( - navigate(`/cases/${id.toString()}`)}> - - - {title} - - - + <> + {!isList ? ( + navigate(`/cases/${id.toString()}`)}> + + + {title} + + + + ) : ( + navigate(`/cases/${id.toString()}`)}> + + + + + + + + + )} + > ); }; diff --git a/web/src/components/EvidenceCard.tsx b/web/src/components/EvidenceCard.tsx index 44a1cabeb..c6f8d0154 100644 --- a/web/src/components/EvidenceCard.tsx +++ b/web/src/components/EvidenceCard.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import Identicon from "react-identicons"; import { Card } from "@kleros/ui-components-library"; import AttachmentIcon from "svgs/icons/attachment.svg"; @@ -13,7 +14,7 @@ const StyledCard = styled(Card)` `; const TextContainer = styled.div` - padding: 8px; + padding: calc(8px + (24 - 8) * (min(max(100vw, 375px), 1250px) - 375px) / 875); > * { overflow-wrap: break-word; margin: 0; @@ -32,8 +33,8 @@ const BottomShade = styled.div` background-color: ${({ theme }) => theme.lightBlue}; display: flex; align-items: center; - gap: 8px; - padding: 8px; + gap: 16px; + padding: 12px calc(8px + (24 - 8) * (min(max(100vw, 375px), 1250px) - 375px) / 875); > * { flex-basis: 1; flex-shrink: 0; @@ -42,15 +43,62 @@ const BottomShade = styled.div` `; const StyledA = styled.a` + display: flex; margin-left: auto; - margin-right: 8px; + gap: calc(5px + (6 - 5) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + + ${landscapeStyle( + () => css` + > svg { + width: 16px; + fill: ${({ theme }) => theme.primaryBlue}; + } + ` + )} +`; + +const AccountContainer = styled.div` display: flex; - > svg { - width: 16px; - fill: ${({ theme }) => theme.primaryBlue}; + flex-direction: row; + gap: 8px; + align-items: center; + + canvas { + width: 24px; + height: 24px; + } + + > * { + flex-basis: 1; + flex-shrink: 0; + margin: 0; } `; +const DesktopText = styled.span` + display: none; + ${landscapeStyle( + () => css` + display: inline; + ` + )} +`; + +const MobileText = styled.span` + ${landscapeStyle( + () => css` + display: none; + ` + )} +`; + +const AttachedFileText: React.FC = () => ( + <> + View attached file + File + > +); + interface IEvidenceCard { evidence: string; sender: string; @@ -73,11 +121,14 @@ const EvidenceCard: React.FC = ({ evidence, sender, index }) => { )} - - {shortenAddress(sender)} + + + {shortenAddress(sender)} + {data && typeof data.fileURI !== "undefined" && ( + )} diff --git a/web/src/components/Field.tsx b/web/src/components/Field.tsx index af7b09df8..15a08ce6e 100644 --- a/web/src/components/Field.tsx +++ b/web/src/components/Field.tsx @@ -1,15 +1,17 @@ import React from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; +import { useFiltersContext } from "context/FilterProvider"; const FieldContainer = styled.div` - width: ${({ width = "100%" }) => width}; + width: ${({ isList }) => (isList ? "auto" : "100%")}; display: flex; align-items: center; justify-content: flex-start; + white-space: nowrap; .value { - flex-grow: 1; - text-align: end; + flex-grow: ${({ isList }) => (isList ? "0" : "1")}; + text-align: ${({ isList }) => (isList ? "center" : "end")}; color: ${({ theme }) => theme.primaryText}; } svg { @@ -27,6 +29,7 @@ const FieldContainer = styled.div` type FieldContainerProps = { width?: string; + isList?: boolean; }; interface IField { @@ -37,18 +40,25 @@ interface IField { width?: string; } -const Field: React.FC = ({ icon: Icon, name, value, link, width }) => ( - - {} - {name}: - {link ? ( - - {value} - - ) : ( - {value} - )} - -); +const Field: React.FC = ({ icon: Icon, name, value, link, width }) => { + const { isList } = useFiltersContext(); + return ( + + {!isList && ( + <> + + {name}: + > + )} + {link ? ( + + {value} + + ) : ( + {value} + )} + + ); +}; export default Field; diff --git a/web/src/components/Popup/index.tsx b/web/src/components/Popup/index.tsx index a289afe1c..92cf5b46c 100644 --- a/web/src/components/Popup/index.tsx +++ b/web/src/components/Popup/index.tsx @@ -1,5 +1,6 @@ import React, { useRef } from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { Button } from "@kleros/ui-components-library"; import { Overlay } from "components/Overlay"; import StakeWithdraw from "./Description/StakeWithdraw"; @@ -44,6 +45,8 @@ const Container = styled.div` top: 50%; left: 50%; transform: translate(-50%, -50%); + max-height: 80vh; + overflow-y: auto; z-index: 10; flex-direction: column; @@ -59,6 +62,12 @@ const Container = styled.div` svg { visibility: visible; } + + ${landscapeStyle( + () => css` + overflow-y: hidden; + ` + )} `; const VoteDescriptionContainer = styled.div` diff --git a/web/src/components/StatDisplay.tsx b/web/src/components/StatDisplay.tsx index 43c929663..79d1073c6 100644 --- a/web/src/components/StatDisplay.tsx +++ b/web/src/components/StatDisplay.tsx @@ -1,10 +1,18 @@ import React from "react"; -import styled, { useTheme } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; +import styled, { useTheme, css } from "styled-components"; const Container = styled.div` display: flex; + max-width: 196px; align-items: center; gap: 8px; + + ${landscapeStyle( + () => css` + margin-bottom: calc(16px + (30 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + ` + )} `; const SVGContainer = styled.div<{ iconColor: string; backgroundColor: string }>` diff --git a/web/src/components/Verdict/DisputeTimeline.tsx b/web/src/components/Verdict/DisputeTimeline.tsx index c2efa61e1..2daad4db0 100644 --- a/web/src/components/Verdict/DisputeTimeline.tsx +++ b/web/src/components/Verdict/DisputeTimeline.tsx @@ -16,19 +16,17 @@ const Container = styled.div` display: flex; position: relative; margin-left: 8px; + flex-direction: column; `; const StyledTimeline = styled(CustomTimeline)` width: 100%; - margin-bottom: 32px; `; const EnforcementContainer = styled.div` - position: absolute; - bottom: 0; display: flex; gap: 8px; - margin-bottom: 8px; + margin-top: calc(12px + (24 - 12) * (min(max(100vw, 375px), 1250px) - 375px) / 875); fill: ${({ theme }) => theme.secondaryText}; small { diff --git a/web/src/components/Verdict/FinalDecision.tsx b/web/src/components/Verdict/FinalDecision.tsx index 5c40e5d09..dfb30960c 100644 --- a/web/src/components/Verdict/FinalDecision.tsx +++ b/web/src/components/Verdict/FinalDecision.tsx @@ -19,6 +19,7 @@ const JuryContainer = styled.div` gap: 8px; h3 { line-height: 21px; + margin-bottom: 0px; } `; @@ -28,10 +29,6 @@ const JuryDecisionTag = styled.small` color: ${({ theme }) => theme.secondaryText}; `; -const Divider = styled.hr` - color: ${({ theme }) => theme.stroke}; -`; - const UserContainer = styled.div` display: flex; align-items: center; @@ -65,12 +62,19 @@ const StyledButton = styled(LightButton)` > .button-text { color: ${({ theme }) => theme.primaryBlue}; } + padding-top: 0px; `; const AnswerTitle = styled.h3` margin: 0; `; +const Divider = styled.hr` + display: flex; + color: ${({ theme }) => theme.stroke}; + margin: calc(16px + (32 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875) 0px; +`; + interface IFinalDecision { arbitrable?: `0x${string}`; } diff --git a/web/src/components/Verdict/VerdictBanner.tsx b/web/src/components/Verdict/VerdictBanner.tsx index 8cfa591f0..516b0779f 100644 --- a/web/src/components/Verdict/VerdictBanner.tsx +++ b/web/src/components/Verdict/VerdictBanner.tsx @@ -6,7 +6,7 @@ import HourglassIcon from "assets/svgs/icons/hourglass.svg"; const BannerContainer = styled.div` display: flex; gap: 8px; - margin: 16px 0px; + margin-bottom: calc(16px + (24 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); svg { width: 16px; height: 16px; diff --git a/web/src/consts/socialmedia.tsx b/web/src/consts/socialmedia.tsx index bd6ffebb7..749e11396 100644 --- a/web/src/consts/socialmedia.tsx +++ b/web/src/consts/socialmedia.tsx @@ -3,7 +3,7 @@ import EtherscanLogo from "svgs/socialmedia/etherscan.svg"; import GithubLogo from "svgs/socialmedia/github.svg"; import SnapshotLogo from "svgs/socialmedia/snapshot.svg"; import DiscordLogo from "svgs/socialmedia/discord.svg"; -import TwitterLogo from "svgs/socialmedia/twitter.svg"; +import XLogo from "svgs/socialmedia/x.svg"; import RedditLogo from "svgs/socialmedia/reddit.svg"; import TelegramLogo from "svgs/socialmedia/telegram.svg"; import GhostBlogLogo from "svgs/socialmedia/ghost-blog.svg"; @@ -26,9 +26,9 @@ export const socialmedia = { icon: , url: "https://discord.com/invite/MhXQGCyHd9", }, - twitter: { - icon: , - url: "https://twitter.com/kleros_io", + x: { + icon: , + url: "https://x.com/kleros_io", }, reddit: { icon: , diff --git a/web/src/context/FilterProvider.tsx b/web/src/context/FilterProvider.tsx new file mode 100644 index 000000000..267059c5e --- /dev/null +++ b/web/src/context/FilterProvider.tsx @@ -0,0 +1,25 @@ +import React, { useState, createContext, useContext } from "react"; + +interface IFilters { + isList: boolean; + setIsList: (arg0: boolean) => void; +} + +const Context = createContext({ + isList: false, + setIsList: () => { + // + }, +}); + +export const FilterProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => { + const [isList, setIsList] = useState(false); + + const value = { + isList, + setIsList, + }; + return {children}; +}; + +export const useFiltersContext = () => useContext(Context); diff --git a/web/src/context/OverlayScrollContext.tsx b/web/src/context/OverlayScrollContext.tsx new file mode 100644 index 000000000..7b9629317 --- /dev/null +++ b/web/src/context/OverlayScrollContext.tsx @@ -0,0 +1,3 @@ +import { createContext, MutableRefObject } from "react"; + +export const OverlayScrollContext = createContext | null>(null); diff --git a/web/src/context/StyledComponentsProvider.tsx b/web/src/context/StyledComponentsProvider.tsx index f5d88f952..7d11c3407 100644 --- a/web/src/context/StyledComponentsProvider.tsx +++ b/web/src/context/StyledComponentsProvider.tsx @@ -8,7 +8,7 @@ import { lightTheme, darkTheme } from "styles/themes"; const StyledComponentsProvider: React.FC<{ children: React.ReactNode; }> = ({ children }) => { - const [theme, setTheme] = useLocalStorage("theme", "light"); + const [theme, setTheme] = useLocalStorage("theme", "dark"); const toggleTheme = () => { if (theme === "light") setTheme("dark"); else setTheme("light"); diff --git a/web/src/context/Web3Provider.tsx b/web/src/context/Web3Provider.tsx index 1677ebbac..c6e161578 100644 --- a/web/src/context/Web3Provider.tsx +++ b/web/src/context/Web3Provider.tsx @@ -8,7 +8,7 @@ import { jsonRpcProvider } from "wagmi/providers/jsonRpc"; import { useToggleTheme } from "hooks/useToggleThemeContext"; import { useTheme } from "styled-components"; -const chains = [mainnet, arbitrumGoerli, gnosisChiado]; +const chains = [arbitrumGoerli, gnosisChiado]; const projectId = process.env.WALLETCONNECT_PROJECT_ID ?? "6efaa26765fa742153baf9281e218217"; const { publicClient, webSocketPublicClient } = configureChains(chains, [ diff --git a/web/src/hooks/queries/useCasesQuery.ts b/web/src/hooks/queries/useCasesQuery.ts index c6b813924..6d1cf7a9e 100644 --- a/web/src/hooks/queries/useCasesQuery.ts +++ b/web/src/hooks/queries/useCasesQuery.ts @@ -5,8 +5,8 @@ import { graphqlQueryFnHelper } from "~src/utils/graphqlQueryFnHelper"; export type { CasesPageQuery }; const casesQuery = graphql(` - query CasesPage($skip: Int) { - disputes(first: 3, skip: $skip, orderBy: lastPeriodChange, orderDirection: desc) { + query CasesPage($first: Int, $skip: Int) { + disputes(first: $first, skip: $skip, orderBy: lastPeriodChange, orderDirection: desc) { id arbitrated { id @@ -26,12 +26,12 @@ const casesQuery = graphql(` } `); -export const useCasesQuery = (skip: number) => { +export const useCasesQuery = (skip: number, first = 3) => { const isEnabled = skip !== undefined; return useQuery({ - queryKey: [`useCasesQuery${skip}`], + queryKey: [`useCasesQuery${skip},${first}`], enabled: isEnabled, - queryFn: async () => await graphqlQueryFnHelper(casesQuery, { skip: skip }), + queryFn: async () => await graphqlQueryFnHelper(casesQuery, { skip, first }), }); }; diff --git a/web/src/hooks/useLockOverlayScroll.ts b/web/src/hooks/useLockOverlayScroll.ts new file mode 100644 index 000000000..146cdcd2a --- /dev/null +++ b/web/src/hooks/useLockOverlayScroll.ts @@ -0,0 +1,28 @@ +import { useContext, useEffect, useCallback } from "react"; +import { OverlayScrollContext } from "context/OverlayScrollContext"; + +export const useLockOverlayScroll = (shouldLock: boolean) => { + const osInstanceRef = useContext(OverlayScrollContext); + + const lockScroll = useCallback(() => { + const osInstance = osInstanceRef?.current?.osInstance(); + if (osInstance) { + osInstance.options({ overflow: { x: "hidden", y: "hidden" } }); + } + }, [osInstanceRef]); + + const unlockScroll = useCallback(() => { + const osInstance = osInstanceRef?.current?.osInstance(); + if (osInstance) { + osInstance.options({ overflow: { x: "scroll", y: "scroll" } }); + } + }, [osInstanceRef]); + + useEffect(() => { + if (shouldLock) { + lockScroll(); + } else { + unlockScroll(); + } + }, [shouldLock, lockScroll, unlockScroll]); +}; diff --git a/web/src/layout/Footer/index.tsx b/web/src/layout/Footer/index.tsx index fe5bc92d8..e6d27e72c 100644 --- a/web/src/layout/Footer/index.tsx +++ b/web/src/layout/Footer/index.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import SecuredByKlerosLogo from "svgs/footer/secured-by-kleros.svg"; import { socialmedia } from "consts/socialmedia"; @@ -9,10 +10,19 @@ const Container = styled.div` background-color: ${({ theme }) => theme.primaryPurple}; display: flex; flex-direction: column; - align-items: center; justify-content: center; + align-items: center; + padding: 0 32px; gap: 16px; + ${landscapeStyle( + () => css` + height: 64px; + flex-direction: row; + justify-content: space-between; + ` + )} + .secured-by-kleros { min-height: 24px; } diff --git a/web/src/layout/Header/DesktopHeader.tsx b/web/src/layout/Header/DesktopHeader.tsx new file mode 100644 index 000000000..d118fa85b --- /dev/null +++ b/web/src/layout/Header/DesktopHeader.tsx @@ -0,0 +1,124 @@ +import React from "react"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; +import { useToggle } from "react-use"; +import { Link } from "react-router-dom"; +import { useLockOverlayScroll } from "hooks/useLockOverlayScroll"; +import KlerosSolutionsIcon from "svgs/menu-icons/kleros-solutions.svg"; +import KlerosCourtLogo from "svgs/header/kleros-court.svg"; +import ConnectWallet from "components/ConnectWallet"; +import LightButton from "components/LightButton"; +import DappList from "./navbar/DappList"; +import Explore from "./navbar/Explore"; +import Menu from "./navbar/Menu"; +import Help from "./navbar/Menu/Help"; +import Settings from "./navbar/Menu/Settings"; +import { Overlay } from "components/Overlay"; +import { PopupContainer } from "."; + +const Container = styled.div` + display: none; + position: absolute; + + ${landscapeStyle( + () => css` + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + position: relative; + ` + )}; +`; + +const LeftSide = styled.div` + display: flex; +`; + +const MiddleSide = styled.div` + display: flex; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + color: ${({ theme }) => theme.white} !important; +`; + +const RightSide = styled.div` + display: flex; + gap: calc(8px + (16 - 8) * ((100vw - 300px) / (1024 - 300))); + margin-left: 8px; + canvas { + width: 20px; + } +`; + +const LightButtonContainer = styled.div` + display: flex; + align-items: center; + width: 16px; + margin-left: calc(4px + (8 - 4) * ((100vw - 375px) / (1250 - 375))); + margin-right: calc(12px + (16 - 12) * ((100vw - 375px) / (1250 - 375))); +`; + +const StyledLink = styled(Link)` + min-height: 48px; +`; + +const StyledKlerosSolutionsIcon = styled(KlerosSolutionsIcon)` + fill: ${({ theme }) => theme.white} !important; +`; + +const ConnectWalletContainer = styled.div` + label { + color: ${({ theme }) => theme.white}; + } +`; + +const DesktopHeader = () => { + const [isDappListOpen, toggleIsDappListOpen] = useToggle(false); + const [isHelpOpen, toggleIsHelpOpen] = useToggle(false); + const [isSettingsOpen, toggleIsSettingsOpen] = useToggle(false); + useLockOverlayScroll(isDappListOpen || isHelpOpen || isSettingsOpen); + + return ( + <> + + + + { + toggleIsDappListOpen(); + }} + Icon={StyledKlerosSolutionsIcon} + /> + + + + + + + + + + + + + + + + + + {(isDappListOpen || isHelpOpen || isSettingsOpen) && ( + + + {isDappListOpen && } + {isHelpOpen && } + {isSettingsOpen && } + + )} + > + ); +}; +export default DesktopHeader; diff --git a/web/src/layout/Header/MobileHeader.tsx b/web/src/layout/Header/MobileHeader.tsx new file mode 100644 index 000000000..4cc4d115c --- /dev/null +++ b/web/src/layout/Header/MobileHeader.tsx @@ -0,0 +1,69 @@ +import React, { useContext, useMemo, useRef } from "react"; +import styled, { css } from "styled-components"; +import { useToggle } from "react-use"; +import { landscapeStyle } from "styles/landscapeStyle"; +import { Link } from "react-router-dom"; +import KlerosCourtLogo from "svgs/header/kleros-court.svg"; +import HamburgerIcon from "svgs/header/hamburger.svg"; +import LightButton from "components/LightButton"; +import NavBar from "./navbar"; +import { useFocusOutside } from "hooks/useFocusOutside"; + +const Container = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + + ${landscapeStyle( + () => css` + display: none; + ` + )} +`; + +const StyledLightButton = styled(LightButton)` + padding: 0; + + .button-svg { + margin-right: 0px; + fill: white; + } + .button-text { + display: none; + } +`; + +const StyledLink = styled(Link)` + min-height: 48px; +`; + +const OpenContext = React.createContext({ + isOpen: false, + toggleIsOpen: () => { + // Placeholder + }, +}); + +export function useOpenContext() { + return useContext(OpenContext); +} + +const MobileHeader = () => { + const [isOpen, toggleIsOpen] = useToggle(false); + const containerRef = useRef(null); + useFocusOutside(containerRef, () => toggleIsOpen(false)); + const memoizedContext = useMemo(() => ({ isOpen, toggleIsOpen }), [isOpen, toggleIsOpen]); + return ( + + + + + + + + + + ); +}; +export default MobileHeader; diff --git a/web/src/layout/Header/index.tsx b/web/src/layout/Header/index.tsx index e4c0b61d0..6287381e6 100644 --- a/web/src/layout/Header/index.tsx +++ b/web/src/layout/Header/index.tsx @@ -1,11 +1,7 @@ -import React, { useState, useRef, useContext } from "react"; +import React from "react"; import styled from "styled-components"; -import { Link } from "react-router-dom"; -import HamburgerIcon from "svgs/header/hamburger.svg"; -import KlerosCourtLogo from "svgs/header/kleros-court.svg"; -import LightButton from "components/LightButton"; -import NavBar from "./navbar"; -import { useFocusOutside } from "hooks/useFocusOutside"; +import MobileHeader from "./MobileHeader"; +import DesktopHeader from "./DesktopHeader"; const Container = styled.div` position: sticky; @@ -17,53 +13,22 @@ const Container = styled.div` padding: 0 24px; display: flex; - align-items: center; - justify-content: space-between; - - .kleros-court-link { - min-height: 48px; - } `; -const StyledLightButton = styled(LightButton)` - padding: 0; - - .button-svg { - margin-right: 0px; - fill: white; - } - .button-text { - display: none; - } +export const PopupContainer = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 30; `; -const OpenContext = React.createContext({ - isOpen: false, - toggleIsOpen: () => { - // Placeholder - }, -}); - -export function useOpenContext() { - return useContext(OpenContext); -} - const Header: React.FC = () => { - const [isOpen, setIsOpen] = useState(false); - const toggleIsOpen = () => setIsOpen(!isOpen); - const containerRef = useRef(null); - useFocusOutside(containerRef, () => setIsOpen(false)); return ( - - - - - - - - - + + ); }; diff --git a/web/src/layout/Header/navbar/DappList.tsx b/web/src/layout/Header/navbar/DappList.tsx index 81c69a38b..17c231f0d 100644 --- a/web/src/layout/Header/navbar/DappList.tsx +++ b/web/src/layout/Header/navbar/DappList.tsx @@ -1,5 +1,6 @@ import React, { useRef } from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { useFocusOutside } from "hooks/useFocusOutside"; import Curate from "svgs/icons/curate-image.png"; import Resolver from "svgs/icons/dispute-resolver.svg"; @@ -8,9 +9,9 @@ import Governor from "svgs/icons/governor.svg"; import Court from "svgs/icons/kleros.svg"; import Linguo from "svgs/icons/linguo.svg"; import POH from "svgs/icons/poh-image.png"; +import Vea from "svgs/icons/vea.svg"; import Tokens from "svgs/icons/tokens.svg"; import Product from "./Product"; -import { Overlay } from "components/Overlay"; const Header = styled.h1` display: flex; @@ -24,11 +25,11 @@ const Header = styled.h1` const Container = styled.div` display: flex; position: absolute; - max-height: 60vh; + max-height: 80vh; top: 5%; left: 50%; transform: translate(-50%); - z-index: 10; + z-index: 1; flex-direction: column; align-items: center; @@ -43,6 +44,16 @@ const Container = styled.div` svg { visibility: visible; } + + ${landscapeStyle( + () => css` + margin-top: 64px; + top: 0; + left: 0; + right: auto; + transform: none; + ` + )} `; const ItemsDiv = styled.div` @@ -58,26 +69,22 @@ const ItemsDiv = styled.div` grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); `; -interface IDappList { - toggleSolution: () => void; -} - const ITEMS = [ { text: "Court v1", Icon: Court, url: "https://court.kleros.io/", }, + { + text: "Vea", + Icon: Vea, + url: "https://veascan.io", + }, { text: "Escrow", Icon: Escrow, url: "https://escrow.kleros.io", }, - { - text: "Tokens", - Icon: Tokens, - url: "https://tokens.kleros.io", - }, { text: "POH", Icon: POH, @@ -88,6 +95,11 @@ const ITEMS = [ Icon: Curate, url: "https://curate.kleros.io", }, + { + text: "Tokens", + Icon: Tokens, + url: "https://tokens.kleros.io", + }, { text: "Resolver", Icon: Resolver, @@ -105,24 +117,25 @@ const ITEMS = [ }, ]; -const DappList: React.FC = ({ toggleSolution }) => { +interface IDappList { + toggleIsDappListOpen: () => void; +} + +const DappList: React.FC = ({ toggleIsDappListOpen }) => { const containerRef = useRef(null); useFocusOutside(containerRef, () => { - toggleSolution(); + toggleIsDappListOpen(); }); return ( - <> - - - Kleros Solutions - - {ITEMS.map((item) => { - return ; - })} - - - > + + Kleros Solutions + + {ITEMS.map((item) => { + return ; + })} + + ); }; export default DappList; diff --git a/web/src/layout/Header/navbar/Explore.tsx b/web/src/layout/Header/navbar/Explore.tsx index 87cf63d12..816e3b643 100644 --- a/web/src/layout/Header/navbar/Explore.tsx +++ b/web/src/layout/Header/navbar/Explore.tsx @@ -1,20 +1,50 @@ import React from "react"; -import styled from "styled-components"; -import { Link } from "react-router-dom"; -import { useOpenContext } from "../index"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; +import { Link, useLocation } from "react-router-dom"; +import { useOpenContext } from "../MobileHeader"; -const Container = styled.div``; +const Container = styled.div` + display: flex; + gap: 0px; + flex-direction: column; + + ${landscapeStyle( + () => css` + flex-direction: row; + gap: calc(4px + (16 - 4) * ((100vw - 375px) / (1250 - 375))); + ` + )}; +`; const LinkContainer = styled.div` - min-height: 32px; display: flex; + min-height: 32px; align-items: center; +`; - .sm-link { - color: ${({ theme }) => theme.primaryText}; - text-decoration: none; - font-size: 16px; - } +const Title = styled.h1` + display: block; + + ${landscapeStyle( + () => css` + display: none; + ` + )}; +`; + +const StyledLink = styled(Link)<{ isActive: boolean }>` + color: ${({ theme }) => theme.primaryText}; + text-decoration: none; + font-size: 16px; + + font-weight: ${({ isActive }) => (isActive ? "600" : "normal")}; + + ${landscapeStyle( + () => css` + color: ${({ theme }) => theme.white}; + ` + )}; `; const links = [ @@ -24,15 +54,17 @@ const links = [ ]; const Explore: React.FC = () => { + const location = useLocation(); const { toggleIsOpen } = useOpenContext(); + return ( - Explore + Explore {links.map(({ to, text }) => ( - + {text} - + ))} diff --git a/web/src/layout/Header/navbar/Menu/Help.tsx b/web/src/layout/Header/navbar/Menu/Help.tsx index 1559d6b68..a242ba067 100644 --- a/web/src/layout/Header/navbar/Menu/Help.tsx +++ b/web/src/layout/Header/navbar/Menu/Help.tsx @@ -1,5 +1,6 @@ import React, { useRef } from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { useFocusOutside } from "hooks/useFocusOutside"; import Book from "svgs/icons/book-open.svg"; import Guide from "svgs/icons/book.svg"; @@ -7,22 +8,35 @@ import Bug from "svgs/icons/bug.svg"; import ETH from "svgs/icons/eth.svg"; import Faq from "svgs/menu-icons/help.svg"; import Telegram from "svgs/socialmedia/telegram.svg"; -import { Overlay } from "components/Overlay"; const Container = styled.div` display: flex; flex-direction: column; position: absolute; + max-height: 80vh; + overflow-y: auto; + width: auto; top: 5%; left: 50%; - transform: translate(-50%); - z-index: 10; + transform: translateX(-50%); + z-index: 1; padding: 27px 10px; gap: 23px; border: 1px solid ${({ theme }) => theme.stroke}; background-color: ${({ theme }) => theme.whiteBackground}; border-radius: 3px; box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.06); + + ${landscapeStyle( + () => css` + margin-top: 64px; + width: 240px; + top: 0; + right: 0; + left: auto; + transform: none; + ` + )} `; const ListItem = styled.a` @@ -83,27 +97,24 @@ const ITEMS = [ ]; interface IHelp { - toggle: () => void; + toggleIsHelpOpen: () => void; } -const Help: React.FC = ({ toggle }) => { +const Help: React.FC = ({ toggleIsHelpOpen }) => { const containerRef = useRef(null); useFocusOutside(containerRef, () => { - toggle(); + toggleIsHelpOpen(); }); return ( - <> - - - {ITEMS.map((item) => ( - - - {item.text} - - ))} - - > + + {ITEMS.map((item) => ( + + + {item.text} + + ))} + ); }; export default Help; diff --git a/web/src/layout/Header/navbar/Menu/Settings/index.tsx b/web/src/layout/Header/navbar/Menu/Settings/index.tsx index a6bf761d0..91a8faba6 100644 --- a/web/src/layout/Header/navbar/Menu/Settings/index.tsx +++ b/web/src/layout/Header/navbar/Menu/Settings/index.tsx @@ -1,23 +1,37 @@ -import React, { Dispatch, SetStateAction, useRef, useState } from "react"; -import styled from "styled-components"; +import React, { useRef, useState } from "react"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { Tabs } from "@kleros/ui-components-library"; import General from "./General"; import SendMeNotifications from "./SendMeNotifications"; import { useFocusOutside } from "hooks/useFocusOutside"; -import { Overlay } from "components/Overlay"; const Container = styled.div` display: flex; position: absolute; + max-height: 80vh; + overflow-y: auto; z-index: 1; background-color: ${({ theme }) => theme.whiteBackground}; flex-direction: column; - border: 1px solid ${({ theme }) => theme.stroke}; - border-radius: 3px; - overflow-y: auto; top: 5%; left: 50%; transform: translateX(-50%); + z-index: 1; + background-color: ${({ theme }) => theme.whiteBackground}; + border: 1px solid ${({ theme }) => theme.stroke}; + border-radius: 3px; + overflow-y: auto; + + ${landscapeStyle( + () => css` + margin-top: 64px; + top: 0; + right: 0; + left: auto; + transform: none; + ` + )} `; const StyledSettingsText = styled.div` @@ -45,29 +59,26 @@ const TABS = [ ]; interface ISettings { - setIsSettingsOpen: Dispatch>; + toggleIsSettingsOpen: () => void; } -const Settings: React.FC = ({ setIsSettingsOpen }) => { +const Settings: React.FC = ({ toggleIsSettingsOpen }) => { const [currentTab, setCurrentTab] = useState(0); const containerRef = useRef(null); - useFocusOutside(containerRef, () => setIsSettingsOpen(false)); + useFocusOutside(containerRef, () => toggleIsSettingsOpen()); return ( - <> - - - Settings - { - setCurrentTab(n); - }} - /> - {currentTab === 0 ? : } - - > + + Settings + { + setCurrentTab(n); + }} + /> + {currentTab === 0 ? : } + ); }; diff --git a/web/src/layout/Header/navbar/Menu/index.tsx b/web/src/layout/Header/navbar/Menu/index.tsx index 2886c7c66..6d749e1a5 100644 --- a/web/src/layout/Header/navbar/Menu/index.tsx +++ b/web/src/layout/Header/navbar/Menu/index.tsx @@ -1,36 +1,72 @@ -import React, { useState } from "react"; -import styled from "styled-components"; -import { useToggle } from "react-use"; +import React from "react"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import LightButton from "components/LightButton"; -import Help from "./Help"; import DarkModeIcon from "svgs/menu-icons/dark-mode.svg"; import HelpIcon from "svgs/menu-icons/help.svg"; import LightModeIcon from "svgs/menu-icons/light-mode.svg"; import NotificationsIcon from "svgs/menu-icons/notifications.svg"; import SettingsIcon from "svgs/menu-icons/settings.svg"; -import Settings from "./Settings"; import { useToggleTheme } from "hooks/useToggleThemeContext"; -const Container = styled.div``; +const Container = styled.div` + display: flex; + + flex-direction: column; + gap: 0px; + + ${landscapeStyle( + () => css` + flex-direction: row; + gap: 8px; + ` + )} +`; const ButtonContainer = styled.div` min-height: 32px; display: flex; align-items: center; + + button { + padding: 0px; + } + + .button-text { + display: block; + } + + .button-svg { + fill: ${({ theme }) => theme.secondaryPurple}; + } + + ${landscapeStyle( + () => css` + .button-svg { + fill: ${({ theme }) => theme.white}; + } + .button-text { + display: none; + } + ` + )} `; -const Menu: React.FC = () => { - const [theme, toggleTheme] = useToggleTheme(); - const [isHelpOpen, toggleIsHelpOpen] = useToggle(true); - const [isSettingsOpen, setIsSettingsOpen] = useState(false); +interface IMenu { + toggleIsSettingsOpen: () => void; + toggleIsHelpOpen: () => void; +} +const Menu: React.FC = ({ toggleIsHelpOpen, toggleIsSettingsOpen }) => { + const [theme, toggleTheme] = useToggleTheme(); const isLightTheme = theme === "light"; + const buttons = [ { text: "Notifications", Icon: NotificationsIcon }, { text: "Settings", Icon: SettingsIcon, - onClick: () => setIsSettingsOpen(true), + onClick: () => toggleIsSettingsOpen(), }, { text: "Help", @@ -49,12 +85,10 @@ const Menu: React.FC = () => { return ( {buttons.map(({ text, Icon, onClick }) => ( - + ))} - {isHelpOpen && } - {isSettingsOpen && } ); }; diff --git a/web/src/layout/Header/navbar/Product.tsx b/web/src/layout/Header/navbar/Product.tsx index 1fa47844c..0dd5c3cf5 100644 --- a/web/src/layout/Header/navbar/Product.tsx +++ b/web/src/layout/Header/navbar/Product.tsx @@ -24,24 +24,9 @@ const Container = styled.a` line-height: 19px; font-size: 14px; } - - svg { - max-width: 48px; - max-height: 48px; - fill: none; - visibility: visible; - display: inline-block; - } `; -const StyledIcon = styled.svg` - max-width: 48px; - max-height: 48px; - fill: none; - visibility: visible; - display: inline-block; - fill: ${({ theme }) => theme.secondaryPurple}; -`; +const StyledIcon = styled.svg``; const StyledImg = styled.img` max-width: 48px; diff --git a/web/src/layout/Header/navbar/index.tsx b/web/src/layout/Header/navbar/index.tsx index 684a36ebf..5a91c3338 100644 --- a/web/src/layout/Header/navbar/index.tsx +++ b/web/src/layout/Header/navbar/index.tsx @@ -1,32 +1,39 @@ import React from "react"; import styled from "styled-components"; -import { useLockBodyScroll, useToggle } from "react-use"; +import { useToggle } from "react-use"; +import { useAccount } from "wagmi"; +import { useLockOverlayScroll } from "hooks/useLockOverlayScroll"; +import { useOpenContext } from "../MobileHeader"; +import DappList from "./DappList"; +import Explore from "./Explore"; import ConnectWallet from "components/ConnectWallet"; import LightButton from "components/LightButton"; +import { Overlay } from "components/Overlay"; import KlerosSolutionsIcon from "svgs/menu-icons/kleros-solutions.svg"; -import { useOpenContext } from "../index"; -import DappList from "./DappList"; -import Explore from "./Explore"; import Menu from "./Menu"; import Debug from "./Debug"; +import Help from "./Menu/Help"; +import Settings from "./Menu/Settings"; +import { DisconnectWalletButton } from "./Menu/Settings/General"; +import { PopupContainer } from ".."; const Container = styled.div<{ isOpen: boolean }>` position: absolute; top: 64px; left: 0; right: 0; + max-height: calc(100vh - 64px); + overflow-y: auto; z-index: 1; background-color: ${({ theme }) => theme.whiteBackground}; border: 1px solid ${({ theme }) => theme.stroke}; box-shadow: 0px 2px 3px ${({ theme }) => theme.defaultShadow}; - transform-origin: top; transform: scaleY(${({ isOpen }) => (isOpen ? "1" : "0")}); visibility: ${({ isOpen }) => (isOpen ? "visible" : "hidden")}; transition-property: transform, visibility; transition-duration: ${({ theme }) => theme.transitionSpeed}; transition-timing-function: ease; - padding: 24px; hr { @@ -34,30 +41,61 @@ const Container = styled.div<{ isOpen: boolean }>` } `; +const WalletContainer = styled.div` + display: flex; + gap: 16px; + justify-content: space-between; + flex-wrap: wrap; +`; + +const DisconnectWalletButtonContainer = styled.div` + display: flex; + align-items: center; +`; + const NavBar: React.FC = () => { - const [isSolutionsOpen, toggleSolution] = useToggle(false); + const { isConnected } = useAccount(); + const [isDappListOpen, toggleIsDappListOpen] = useToggle(false); + const [isHelpOpen, toggleIsHelpOpen] = useToggle(false); + const [isSettingsOpen, toggleIsSettingsOpen] = useToggle(false); const { isOpen } = useOpenContext(); - useLockBodyScroll(isOpen); + useLockOverlayScroll(isOpen); return ( - - { - toggleSolution(); - }} - Icon={KlerosSolutionsIcon} - /> - {isSolutionsOpen && } - - - - - - - - - + <> + + { + toggleIsDappListOpen(); + }} + Icon={KlerosSolutionsIcon} + /> + + + + + + {isConnected && ( + + + + )} + + + + + + + {(isDappListOpen || isHelpOpen || isSettingsOpen) && ( + + + {isDappListOpen && } + {isHelpOpen && } + {isSettingsOpen && } + + )} + > ); }; diff --git a/web/src/layout/index.tsx b/web/src/layout/index.tsx index 4a50039d3..ecfb3153a 100644 --- a/web/src/layout/index.tsx +++ b/web/src/layout/index.tsx @@ -1,7 +1,10 @@ -import React from "react"; +import React, { useRef } from "react"; import styled from "styled-components"; +import "overlayscrollbars/styles/overlayscrollbars.css"; import { Outlet } from "react-router-dom"; import { ToastContainer } from "react-toastify"; +import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; +import { OverlayScrollContext } from "context/OverlayScrollContext"; import Header from "./Header"; import Footer from "./Footer"; @@ -10,18 +13,31 @@ const Container = styled.div` width: 100%; `; +const StyledOverlayScrollbarsComponent = styled(OverlayScrollbarsComponent)` + height: 100vh; + width: 100vw; +`; + const StyledToastContainer = styled(ToastContainer)` padding: 16px; padding-top: 70px; `; -const Layout: React.FC = () => ( - - - - - - -); +const Layout: React.FC = () => { + const containerRef = useRef(null); + + return ( + + + + + + + + + + + ); +}; export default Layout; diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx index 2645336ea..019e70bff 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx @@ -15,6 +15,12 @@ import { useFundingContext, } from "hooks/useClassicAppealContext"; +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 8px; +`; + const StyledField = styled(Field)` width: 100%; & > input { @@ -86,7 +92,7 @@ const Fund: React.FC = ({ amount, setAmount, setIsOpen }) => { const fundAppeal = useFundAppeal(parsedAmount); return needFund ? ( - + How much ETH do you want to contribute? = ({ amount, setAmount, setIsOpen }) => { /> - + ) : ( <>> ); diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx index 805ec3244..765a1a7e2 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx @@ -8,6 +8,7 @@ import { useOptionsContext, useSelectedOptionContext, } from "hooks/useClassicAppealContext"; +import { formatUnitsWei } from "utils/format"; const Container = styled.div` margin: 24px 0; @@ -20,7 +21,11 @@ const OptionsContainer = styled.div` margin-top: 12px; `; -const StageOne: React.FC = () => { +interface IStageOne { + setAmount: (val: string) => void; +} + +const StageOne: React.FC = ({ setAmount }) => { const { paidFees, winningChoice, loserRequiredFunding, winnerRequiredFunding } = useFundingContext(); const options = useOptionsContext(); const loserSideCountdown = useLoserSideCountdownContext(); @@ -33,17 +38,23 @@ const StageOne: React.FC = () => { {typeof paidFees !== "undefined" && typeof winnerRequiredFunding !== "undefined" && typeof loserRequiredFunding !== "undefined" && - options?.map((answer: string, i: number) => ( - setSelectedOption(i)} - /> - ))} + options?.map((answer: string, i: number) => { + const requiredFunding = i.toString() === winningChoice ? winnerRequiredFunding : loserRequiredFunding; + return ( + { + setSelectedOption(i); + setAmount(formatUnitsWei(requiredFunding)); + }} + /> + ); + })} ); diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx index 6be1c1485..fe7a4ec66 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx @@ -3,6 +3,7 @@ import styled from "styled-components"; import OptionCard from "../../OptionCard"; import { useFundingContext, useOptionsContext, useSelectedOptionContext } from "hooks/useClassicAppealContext"; import { isUndefined } from "utils/index"; +import { formatUnitsWei } from "utils/format"; const Container = styled.div` margin: 24px 0; @@ -15,12 +16,17 @@ const OptionsContainer = styled.div` margin-top: 12px; `; -const StageOne: React.FC = () => { +interface IStageTwo { + setAmount: (val: string) => void; +} + +const StageTwo: React.FC = ({ setAmount }) => { const { paidFees, winningChoice, winnerRequiredFunding, fundedChoices } = useFundingContext(); const options = useOptionsContext(); const { selectedOption, setSelectedOption } = useSelectedOptionContext(); useEffect(() => { if (!isUndefined(winningChoice)) setSelectedOption(parseInt(winningChoice)); + if (!isUndefined(winnerRequiredFunding)) setAmount(formatUnitsWei(winnerRequiredFunding)); }); return ( @@ -50,4 +56,4 @@ const StageOne: React.FC = () => { ); }; -export default StageOne; +export default StageTwo; diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx index 83219b2c1..95d81fc87 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx @@ -10,10 +10,16 @@ const Container = styled.div` margin: 24px 0; `; -const Options: React.FC = () => { +interface IOptions { + setAmount: (val: string) => void; +} + +const Options: React.FC = ({ setAmount }) => { const loserSideCountdown = useLoserSideCountdownContext(); return !isUndefined(loserSideCountdown) ? ( - {loserSideCountdown > 0 ? : } + + {loserSideCountdown > 0 ? : } + ) : ( ); diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx index 123838747..d3fd96015 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx @@ -23,6 +23,7 @@ const CountdownLabel = styled.label` margin-bottom: 12px; border-bottom: 1px solid ${({ theme }) => theme.secondaryPurple}; color: ${({ theme }) => theme.primaryText}; + gap: 8px; & > svg { width: 14px; fill: ${({ theme }) => theme.secondaryPurple}; diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/index.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/index.tsx index 921fac7a9..c5b0a67b2 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/index.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/index.tsx @@ -27,7 +27,7 @@ const Classic: React.FC = () => { )} Appeal crowdfunding The jury decision is appealed when two options are fully funded. - + > ); diff --git a/web/src/pages/Cases/CaseDetails/Appeal/OptionCard.tsx b/web/src/pages/Cases/CaseDetails/Appeal/OptionCard.tsx index df23ed64c..729a402d6 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/OptionCard.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/OptionCard.tsx @@ -18,7 +18,7 @@ const WinnerLabel = styled.label<{ winner: boolean }>` color: ${({ theme, winner }) => (winner ? theme.success : theme.warning)}; svg { width: 12px; - margin-right: 4px; + margin-right: 8px; fill: ${({ theme, winner }) => (winner ? theme.success : theme.warning)}; } `; diff --git a/web/src/pages/Cases/CaseDetails/Overview.tsx b/web/src/pages/Cases/CaseDetails/Overview.tsx index c7e4b0f7f..c6b414ee8 100644 --- a/web/src/pages/Cases/CaseDetails/Overview.tsx +++ b/web/src/pages/Cases/CaseDetails/Overview.tsx @@ -1,12 +1,13 @@ -import React from "react"; -import styled from "styled-components"; +import React, { useEffect } from "react"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { useParams } from "react-router-dom"; import ReactMarkdown from "react-markdown"; import { formatEther } from "viem"; import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery"; import { useDisputeTemplate } from "queries/useDisputeTemplate"; import { useCourtPolicy } from "queries/useCourtPolicy"; -import { useCourtPolicyURI } from "queries/useCourtPolicyURI"; +import { useFiltersContext } from "context/FilterProvider"; import { isUndefined } from "utils/index"; import { Periods } from "consts/periods"; import { IPFS_GATEWAY } from "consts/index"; @@ -21,7 +22,7 @@ const Container = styled.div` height: auto; display: flex; flex-direction: column; - gap: 16px; + gap: calc(16px + (32 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); > h1 { margin: 0; @@ -43,7 +44,14 @@ const QuestionAndDescription = styled.div` const VotingOptions = styled(QuestionAndDescription)` display: flex; flex-direction: column; - > span { + gap: 8px; +`; + +const Answers = styled.div` + display: flex; + flex-direction: column; + + span { margin: 0px; display: flex; gap: 8px; @@ -51,13 +59,29 @@ const VotingOptions = styled(QuestionAndDescription)` `; const ShadeArea = styled.div` + display: flex; + flex-direction: column; + justify-content: center; width: 100%; - padding: 16px; - margin-top: 16px; + padding: calc(16px + (32 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + margin-top: calc(24px + (48 - 24) * (min(max(100vw, 375px), 1250px) - 375px) / 875); background-color: ${({ theme }) => theme.mediumBlue}; > p { margin-top: 0; + margin-bottom: 16px; + ${landscapeStyle( + () => css` + margin-bottom: 0; + ` + )}; } + + ${landscapeStyle( + () => css` + flex-direction: row; + justify-content: space-between; + ` + )}; `; const StyledA = styled.a` @@ -73,6 +97,13 @@ const StyledA = styled.a` const LinkContainer = styled.div` display: flex; justify-content: space-between; + gap: calc(8px + (24 - 8) * (min(max(100vw, 375px), 1250px) - 375px) / 875); +`; + +const Divider = styled.hr` + display: flex; + color: ${({ theme }) => theme.stroke}; + margin: 0; `; interface IOverview { @@ -85,14 +116,20 @@ const Overview: React.FC = ({ arbitrable, courtID, currentPeriodIndex const { id } = useParams(); const { data: disputeTemplate } = useDisputeTemplate(id, arbitrable); const { data: disputeDetails } = useDisputeDetailsQuery(id); - const { data: courtPolicyURI } = useCourtPolicyURI(courtID); const { data: courtPolicy } = useCourtPolicy(courtID); + const { isList, setIsList } = useFiltersContext(); const { data: votingHistory } = useVotingHistory(id); + const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds; const courtName = courtPolicy?.name; const court = disputeDetails?.dispute?.court; const rewards = court ? `≥ ${formatEther(court.feeForJuror)} ETH` : undefined; const category = disputeTemplate ? disputeTemplate.category : undefined; - const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds; + + useEffect(() => { + if (isList) { + setIsList(false); + } + }, []); return ( <> @@ -115,18 +152,20 @@ const Overview: React.FC = ({ arbitrable, courtID, currentPeriodIndex )} {disputeTemplate && Voting Options} - {disputeTemplate?.answers?.map((answer: { title: string; description: string }, i: number) => ( - - Option {i + 1}: - {answer.title} - - ))} + + {disputeTemplate?.answers?.map((answer: { title: string; description: string }, i: number) => ( + + Option {i + 1}: + {answer.title} + + ))} + + {currentPeriodIndex !== Periods.evidence && ( <> - - + > )} diff --git a/web/src/pages/Cases/CaseDetails/Tabs.tsx b/web/src/pages/Cases/CaseDetails/Tabs.tsx index eff5724d0..1321d675e 100644 --- a/web/src/pages/Cases/CaseDetails/Tabs.tsx +++ b/web/src/pages/Cases/CaseDetails/Tabs.tsx @@ -19,7 +19,7 @@ const StyledTabs = styled(TabsComponent)` display: flex; flex-wrap: wrap; > svg { - margin-right: 0px !important; + margin-right: 8px !important; } } `; diff --git a/web/src/pages/Cases/CaseDetails/Timeline.tsx b/web/src/pages/Cases/CaseDetails/Timeline.tsx index 9fb621da0..7f83833a1 100644 --- a/web/src/pages/Cases/CaseDetails/Timeline.tsx +++ b/web/src/pages/Cases/CaseDetails/Timeline.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { Periods } from "consts/periods"; import { DisputeDetailsQuery } from "queries/useDisputeDetailsQuery"; import { Box, Steps } from "@kleros/ui-components-library"; @@ -8,18 +9,48 @@ import { useCountdown } from "hooks/useCountdown"; import { secondsToDayHourMinute } from "utils/date"; const TimeLineContainer = styled(Box)` + display: block; width: 100%; - height: 100px; - border-radius: 3px; - margin: 16px 0px; - padding: 8px; + height: 98px; + border-radius: 0px; + padding: 20px 8px 0px 8px; + margin-top: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + margin-bottom: calc(12px + (22 - 12) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + background-color: ${({ theme }) => theme.whiteBackground}; + + ${landscapeStyle( + () => css` + display: block; + padding: 28px 8px 8px 8px; + ` + )} `; -const StyledSteps = styled(Steps)` +const StyledSteps = styled(Steps)`d + display: flex; + justify-content: space-between; width: 85%; margin: auto; `; +const TitleMobile = styled.span` + ${landscapeStyle( + () => css` + display: none; + ` + )} +`; + +const TitleDesktop = styled(TitleMobile)` + display: none; + + ${landscapeStyle( + () => css` + display: inline-block; + ` + )} +`; + const Timeline: React.FC<{ dispute: DisputeDetailsQuery["dispute"]; currentPeriodIndex: number; @@ -40,7 +71,12 @@ const currentPeriodToCurrentItem = (currentPeriodIndex: number, ruled?: boolean) }; const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex: number, currentPeriodIndex: number) => { - const titles = ["Evidence Period", "Voting Period", "Appeal Period", "Executed"]; + const titles = [ + { mobile: "Evidence", desktop: "Evidence Period" }, + { mobile: "Voting", desktop: "Voting Period" }, + { mobile: "Appeal", desktop: "Appeal Period" }, + { mobile: "Executed", desktop: "Executed" }, + ]; const deadlineCurrentPeriod = getDeadline( currentPeriodIndex, dispute?.lastPeriodChange, @@ -64,7 +100,12 @@ const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex: return []; }; return titles.map((title, i) => ({ - title, + title: ( + <> + {title.mobile} + {title.desktop} + > + ), subitems: getSubitems(i), })); }; diff --git a/web/src/pages/Cases/CaseDetails/Voting/index.tsx b/web/src/pages/Cases/CaseDetails/Voting/index.tsx index 7628685cb..a9dc5add8 100644 --- a/web/src/pages/Cases/CaseDetails/Voting/index.tsx +++ b/web/src/pages/Cases/CaseDetails/Voting/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import styled from "styled-components"; import { useParams } from "react-router-dom"; import { useAccount } from "wagmi"; -import { useLockBodyScroll } from "react-use"; +import { useLockOverlayScroll } from "hooks/useLockOverlayScroll"; import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery"; import { useDrawQuery } from "queries/useDrawQuery"; import { useAppealCost } from "queries/useAppealCost"; @@ -56,7 +56,7 @@ const Voting: React.FC = ({ arbitrable, currentPeriodIndex }) => { watch: true, }); const [isPopupOpen, setIsPopupOpen] = useState(false); - useLockBodyScroll(isPopupOpen); + useLockOverlayScroll(isPopupOpen); const lastPeriodChange = disputeData?.dispute?.lastPeriodChange; const timesPerPeriod = disputeData?.dispute?.court?.timesPerPeriod; const finalDate = diff --git a/web/src/pages/Cases/CaseDetails/index.tsx b/web/src/pages/Cases/CaseDetails/index.tsx index 46dc66017..bf93c17eb 100644 --- a/web/src/pages/Cases/CaseDetails/index.tsx +++ b/web/src/pages/Cases/CaseDetails/index.tsx @@ -14,11 +14,14 @@ import Voting from "./Voting"; const Container = styled.div``; const StyledCard = styled(Card)` - margin-top: 16px; width: 100%; height: auto; min-height: 100px; - padding: 16px; + padding: calc(16px + (32 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); +`; + +const Header = styled.h1` + margin-bottom: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); `; const CaseDetails: React.FC = () => { @@ -30,7 +33,7 @@ const CaseDetails: React.FC = () => { return ( - Case #{id} + Case #{id} diff --git a/web/src/pages/Cases/index.tsx b/web/src/pages/Cases/index.tsx index 86b623fdb..11afa992b 100644 --- a/web/src/pages/Cases/index.tsx +++ b/web/src/pages/Cases/index.tsx @@ -1,21 +1,35 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import styled from "styled-components"; import { Routes, Route } from "react-router-dom"; import { useCasesQuery } from "queries/useCasesQuery"; +import { useWindowSize } from "react-use"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import CasesDisplay from "components/CasesDisplay"; import CaseDetails from "./CaseDetails"; - +import { useFiltersContext } from "context/FilterProvider"; const Container = styled.div` width: 100%; min-height: calc(100vh - 144px); background-color: ${({ theme }) => theme.lightBackground}; - padding: 32px; + padding: calc(32px + (136 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-top: calc(32px + (80 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-bottom: calc(64px + (96 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); `; const Cases: React.FC = () => { const [currentPage, setCurrentPage] = useState(1); - const casesPerPage = 3; - const { data } = useCasesQuery(casesPerPage * (currentPage - 1)); + const { width } = useWindowSize(); + const { isList, setIsList } = useFiltersContext(); + const screenIsBig = width > BREAKPOINT_LANDSCAPE; + const casesPerPage = screenIsBig ? 9 : 3; + const { data } = useCasesQuery(casesPerPage * (currentPage - 1), casesPerPage); + + useEffect(() => { + if (!screenIsBig && isList) { + setIsList(false); + } + }, [screenIsBig, isList, setIsList]); + return ( diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx index 49590e6fe..36c5b8e9c 100644 --- a/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx +++ b/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import styled from "styled-components"; -import { useLockBodyScroll } from "react-use"; +import { useLockOverlayScroll } from "hooks/useLockOverlayScroll"; import Tag from "components/Tag"; import JurorBalanceDisplay from "./JurorStakeDisplay"; import InputDisplay from "./InputDisplay"; @@ -37,7 +37,7 @@ const StakePanel: React.FC<{ courtName: string; id: string }> = ({ courtName = " const [isActive, setIsActive] = useState(true); const [action, setAction] = useState(ActionType.stake); - useLockBodyScroll(isPopupOpen); + useLockOverlayScroll(isPopupOpen); const handleClick = (action: ActionType) => { setIsActive(action === ActionType.stake); diff --git a/web/src/pages/Courts/CourtDetails/Stats.tsx b/web/src/pages/Courts/CourtDetails/Stats.tsx index bfc8bfa49..e910bae51 100644 --- a/web/src/pages/Courts/CourtDetails/Stats.tsx +++ b/web/src/pages/Courts/CourtDetails/Stats.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { useParams } from "react-router-dom"; import { useCourtDetails, CourtDetailsQuery } from "queries/useCourtDetails"; import { useCoinPrice } from "hooks/useCoinPrice"; @@ -22,6 +23,14 @@ const StyledCard = styled.div` display: grid; gap: 32px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + padding: calc(0px + (32 - 0) * (min(max(100vw, 375px), 1250px) - 375px) / 875) 0; + padding-bottom: 0px; + + ${landscapeStyle( + () => css` + gap: 16px; + ` + )} `; interface IStat { @@ -45,8 +54,14 @@ const stats: IStat[] = [ { title: "Vote Stake", coinId: 0, - getText: (data) => formatPNK(data?.minStake), - getSubtext: (data, coinPrice) => formatUSD(Number(formatUnitsWei(data?.minStake)) * (coinPrice ?? 0)), + getText: (data) => { + const stake = BigInt((data?.minStake * data?.alpha) / 1e4); + return formatPNK(stake); + }, + getSubtext: (data, coinPrice) => { + const stake = BigInt((data?.minStake * data?.alpha) / 1e4); + return formatUSD(Number(formatUnitsWei(stake)) * (coinPrice ?? 0)); + }, color: "purple", icon: VoteStake, }, diff --git a/web/src/pages/Courts/CourtDetails/index.tsx b/web/src/pages/Courts/CourtDetails/index.tsx index e8a8db7db..5db6a65a8 100644 --- a/web/src/pages/Courts/CourtDetails/index.tsx +++ b/web/src/pages/Courts/CourtDetails/index.tsx @@ -25,10 +25,10 @@ const ButtonContainer = styled.div` `; const StyledCard = styled(Card)` - margin-top: 16px; + margin-top: calc(16px + (24 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); width: 100%; height: auto; - padding: 16px; + padding: calc(16px + (32 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); min-height: 100px; `; diff --git a/web/src/pages/Courts/index.tsx b/web/src/pages/Courts/index.tsx index e9803fd0c..fef9e6ab1 100644 --- a/web/src/pages/Courts/index.tsx +++ b/web/src/pages/Courts/index.tsx @@ -8,7 +8,9 @@ const Container = styled.div` width: 100%; min-height: calc(100vh - 144px); background-color: ${({ theme }) => theme.lightBackground}; - padding: 32px; + padding: calc(32px + (136 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-top: calc(32px + (80 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-bottom: calc(64px + (96 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); `; const Courts: React.FC = () => { diff --git a/web/src/pages/Dashboard/Courts/index.tsx b/web/src/pages/Dashboard/Courts/index.tsx index aaa4ed1d0..8599c6d38 100644 --- a/web/src/pages/Dashboard/Courts/index.tsx +++ b/web/src/pages/Dashboard/Courts/index.tsx @@ -7,6 +7,10 @@ import { useUserQuery } from "queries/useUser"; const Container = styled.div` margin-top: 64px; + + h1 { + margin-bottom: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + } `; const CourtsContainer = styled.div` @@ -23,7 +27,7 @@ const Courts: React.FC = () => { <> My Courts - + {!isUndefined(data) && } {!isUndefined(data) && data.user?.tokens?.map(({ court: { id, name } }) => { diff --git a/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx b/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx index 57fabfdc8..f51c5b515 100644 --- a/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx +++ b/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx @@ -9,17 +9,11 @@ import { CoinIds } from "consts/coingecko"; import { useUserQuery, UserQuery } from "queries/useUser"; import { useCoinPrice } from "hooks/useCoinPrice"; -interface IReward { - token: "ETH" | "PNK"; - coinId: number; - getAmount: (amount: bigint) => string; - getValue: (amount: bigint, coinPrice?: number) => string; -} const Container = styled.div` display: flex; flex-direction: column; align-items: flex-start; - width: 100%; + width: auto; `; const tooltipMsg = @@ -28,6 +22,13 @@ const tooltipMsg = "is coherent with the final ruling receive the Juror Rewards composed of " + "arbitration fees (ETH) + PNK redistribution between jurors."; +interface IReward { + token: "ETH" | "PNK"; + coinId: number; + getAmount: (amount: bigint) => string; + getValue: (amount: bigint, coinPrice?: number) => string; +} + const rewards: IReward[] = [ { token: "ETH", diff --git a/web/src/pages/Dashboard/JurorInfo/index.tsx b/web/src/pages/Dashboard/JurorInfo/index.tsx index 1628f7c40..64e4636ae 100644 --- a/web/src/pages/Dashboard/JurorInfo/index.tsx +++ b/web/src/pages/Dashboard/JurorInfo/index.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { Card as _Card } from "@kleros/ui-components-library"; import Coherency from "./Coherency"; import JurorRewards from "./JurorRewards"; @@ -7,6 +8,10 @@ import JurorRewards from "./JurorRewards"; const Container = styled.div``; +const Header = styled.h1` + margin-bottom: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); +`; + const Card = styled(_Card)` width: 100%; height: auto; @@ -16,15 +21,24 @@ const Layout = styled.div` display: flex; flex-direction: column; align-items: center; + justify-content: center; + gap: 24px; - width: fit-content; - margin: 16px auto; + width: auto; + margin: 16px 32px; + + ${landscapeStyle( + () => css` + flex-direction: row; + gap: 48px; + ` + )} `; const JurorInfo: React.FC = () => { return ( - Juror Dashboard + Juror Dashboard diff --git a/web/src/pages/Dashboard/index.tsx b/web/src/pages/Dashboard/index.tsx index aaf615cec..7ae82faee 100644 --- a/web/src/pages/Dashboard/index.tsx +++ b/web/src/pages/Dashboard/index.tsx @@ -1,7 +1,10 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import styled from "styled-components"; +import { useWindowSize } from "react-use"; import { useAccount } from "wagmi"; +import { useFiltersContext } from "context/FilterProvider"; import { useCasesQuery } from "queries/useCasesQuery"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import JurorInfo from "./JurorInfo"; import Courts from "./Courts"; import CasesDisplay from "components/CasesDisplay"; @@ -11,11 +14,17 @@ const Container = styled.div` width: 100%; min-height: calc(100vh - 144px); background-color: ${({ theme }) => theme.lightBackground}; - padding: 32px; + padding: calc(32px + (136 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-top: calc(32px + (80 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-bottom: calc(64px + (96 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); `; const StyledCasesDisplay = styled(CasesDisplay)` margin-top: 64px; + + h1 { + margin-bottom: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + } `; const ConnectWalletContainer = styled.div` @@ -28,10 +37,19 @@ const ConnectWalletContainer = styled.div` const Dashboard: React.FC = () => { const { isConnected } = useAccount(); + const { width } = useWindowSize(); + const screenIsBig = width > BREAKPOINT_LANDSCAPE; + const { isList, setIsList } = useFiltersContext(); const [currentPage, setCurrentPage] = useState(1); const casesPerPage = 3; const { data } = useCasesQuery(casesPerPage * (currentPage - 1)); + useEffect(() => { + if (!screenIsBig && isList) { + setIsList(false); + } + }, [screenIsBig, isList, setIsList]); + return ( {isConnected ? ( diff --git a/web/src/pages/Home/Community/index.tsx b/web/src/pages/Home/Community/index.tsx index ccb23d2a1..cbfd5ca44 100644 --- a/web/src/pages/Home/Community/index.tsx +++ b/web/src/pages/Home/Community/index.tsx @@ -1,11 +1,16 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { Card } from "@kleros/ui-components-library"; import { Element } from "./Element"; import { firstSection, secondSection } from "consts/community-elements"; const Container = styled.div` margin-top: 64px; + + h1 { + margin-bottom: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + } `; const StyledCard = styled(Card)` @@ -19,11 +24,37 @@ const StyledSeparator = styled.hr` const Section = styled.div` width: 100%; + gap: 8px; + flex-direction: column; + height: auto; + flex-wrap: wrap; padding: 12px; display: flex; - flex-direction: column; align-items: center; + justify-content: center; + + ${landscapeStyle( + () => css` + flex-direction: row; + justify-content: space-between; + gap: 0px; + padding: 0 32px; + min-height: 64px; + ` + )} +`; + +const TwoElementContainer = styled.div` + display: flex; + flex-direction: column; gap: 8px; + + ${landscapeStyle( + () => css` + flex-direction: row; + gap: 48px; + ` + )} `; const Community = () => ( @@ -31,14 +62,17 @@ const Community = () => ( Community - {firstSection.map((element, i) => ( - - ))} + + {firstSection.slice(0, 2).map((element) => ( + + ))} + + - {secondSection.map((element, i) => ( - + {secondSection.map((element) => ( + ))} diff --git a/web/src/pages/Home/CourtOverview/Chart.tsx b/web/src/pages/Home/CourtOverview/Chart.tsx index 3e8ac0414..5e48f574c 100644 --- a/web/src/pages/Home/CourtOverview/Chart.tsx +++ b/web/src/pages/Home/CourtOverview/Chart.tsx @@ -7,7 +7,7 @@ import { formatUnits } from "viem"; import { useHomePageContext } from "hooks/useHomePageContext"; const Container = styled.div` - margin-bottom: 32px; + margin-bottom: calc(32px + (48 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); display: flex; flex-direction: column; `; diff --git a/web/src/pages/Home/CourtOverview/Stats.tsx b/web/src/pages/Home/CourtOverview/Stats.tsx index 58c665462..6fbd57fa0 100644 --- a/web/src/pages/Home/CourtOverview/Stats.tsx +++ b/web/src/pages/Home/CourtOverview/Stats.tsx @@ -1,5 +1,6 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; +import { landscapeStyle } from "styles/landscapeStyle"; import { Card } from "@kleros/ui-components-library"; import StatDisplay, { IStatDisplay } from "components/StatDisplay"; import { StyledSkeleton } from "components/StyledSkeleton"; @@ -18,10 +19,19 @@ import { useCoinPrice } from "hooks/useCoinPrice"; const StyledCard = styled(Card)` width: auto; height: fit-content; - padding: 16px; - display: grid; gap: 32px; + padding: calc(16px + (30 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-left: calc(16px + (35 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-bottom: 16px; + display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + + ${landscapeStyle( + () => css` + padding-bottom: 0px; + gap: 0px; + ` + )} `; const getLastOrZero = (src: HomePageQuery["counters"], stat: HomePageQueryDataPoints) => diff --git a/web/src/pages/Home/Header.tsx b/web/src/pages/Home/Header.tsx deleted file mode 100644 index 05c825635..000000000 --- a/web/src/pages/Home/Header.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import { useTheme } from "styled-components"; -import { useMeasure } from "react-use"; -import { BREAKPOINT_SMALL_SCREEN } from "styles/smallScreenStyle"; -import HeaderLightMobile from "tsx:svgs/header/header-lightmode-mobile.svg"; -import HeaderDarkMobile from "tsx:svgs/header/header-darkmode-mobile.svg"; -import HeaderLightDesktop from "tsx:svgs/header/header-lightmode-desktop.svg"; -import HeaderDarkDesktop from "tsx:svgs/header/header-darkmode-desktop.svg"; - -const Header = () => { - const [ref, { width }] = useMeasure(); - const theme = useTheme(); - const themeIsLight = theme.name === "light"; - const screenIsBig = width > BREAKPOINT_SMALL_SCREEN; - return ( - - {screenIsBig ? : } - - ); -}; - -const HeaderDesktop: React.FC<{ themeIsLight: boolean }> = ({ themeIsLight }) => { - return themeIsLight ? : ; -}; - -const HeaderMobile: React.FC<{ themeIsLight: boolean }> = ({ themeIsLight }) => { - return themeIsLight ? : ; -}; - -export default Header; diff --git a/web/src/pages/Home/HeroImage.tsx b/web/src/pages/Home/HeroImage.tsx new file mode 100644 index 000000000..3170244b5 --- /dev/null +++ b/web/src/pages/Home/HeroImage.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { useTheme } from "styled-components"; +import { useWindowSize } from "react-use"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; +import HeroLightMobile from "tsx:svgs/hero/hero-lightmode-mobile.svg"; +import HeroDarkMobile from "tsx:svgs/hero/hero-darkmode-mobile.svg"; +import HeroLightDesktop from "tsx:svgs/hero/hero-lightmode-desktop.svg"; +import HeroDarkDesktop from "tsx:svgs/hero/hero-darkmode-desktop.svg"; + +const HeroImage = () => { + const { width } = useWindowSize(); + const theme = useTheme(); + const themeIsLight = theme.name === "light"; + const screenIsBig = width > BREAKPOINT_LANDSCAPE; + return {screenIsBig ? : }; +}; + +const HeroDesktop: React.FC<{ themeIsLight: boolean }> = ({ themeIsLight }) => { + return themeIsLight ? : ; +}; + +const HeroMobile: React.FC<{ themeIsLight: boolean }> = ({ themeIsLight }) => { + return themeIsLight ? : ; +}; + +export default HeroImage; diff --git a/web/src/pages/Home/LatestCases.tsx b/web/src/pages/Home/LatestCases.tsx index d3f9028a6..7ebd3122c 100644 --- a/web/src/pages/Home/LatestCases.tsx +++ b/web/src/pages/Home/LatestCases.tsx @@ -1,21 +1,32 @@ -import React from "react"; +import React, { useEffect } from "react"; import styled from "styled-components"; import { useCasesQuery } from "queries/useCasesQuery"; +import { useFiltersContext } from "context/FilterProvider"; import DisputeCard from "components/DisputeCard"; import { StyledSkeleton } from "components/StyledSkeleton"; const Container = styled.div` - margin-top: 64px; + margin-top: calc(64px + (80 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); .disputes-container { display: flex; gap: 24px; flex-wrap: wrap; justify-content: center; } + + h1 { + margin-bottom: calc(16px + (48 - 16) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + } `; const LatestCases: React.FC = () => { const { data } = useCasesQuery(0); + const { setIsList } = useFiltersContext(); + + useEffect(() => { + setIsList(false); + }, []); + return ( Latest Cases diff --git a/web/src/pages/Home/index.tsx b/web/src/pages/Home/index.tsx index f78437078..cb1168923 100644 --- a/web/src/pages/Home/index.tsx +++ b/web/src/pages/Home/index.tsx @@ -3,7 +3,7 @@ import styled from "styled-components"; import CourtOverview from "./CourtOverview"; import LatestCases from "./LatestCases"; import Community from "./Community"; -import Header from "./Header"; +import HeroImage from "./HeroImage"; import { HomePageProvider } from "hooks/useHomePageContext"; import { getOneYearAgoTimestamp } from "utils/date"; @@ -11,13 +11,15 @@ const Container = styled.div` width: 100%; min-height: calc(100vh - 144px); background-color: ${({ theme }) => theme.lightBackground}; - padding: 32px; + padding: calc(32px + (132 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-top: calc(32px + (72 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-bottom: calc(64px + (96 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); `; const Home: React.FC = () => { return ( - + diff --git a/web/src/styles/global-style.ts b/web/src/styles/global-style.ts index bf9404551..a2a20ec74 100644 --- a/web/src/styles/global-style.ts +++ b/web/src/styles/global-style.ts @@ -103,4 +103,11 @@ export const GlobalStyle = createGlobalStyle` --base-color: ${({ theme }) => theme.skeletonBackground}; --highlight-color: ${({ theme }) => theme.skeletonHighlight}; } + + .os-theme-dark { + --os-handle-bg: ${({ theme }) => theme.violetPurple}; + --os-handle-bg-hover: ${({ theme }) => theme.secondaryPurple}; + --os-handle-bg-active: ${({ theme }) => theme.lavenderPurple}; + } + `; diff --git a/web/src/styles/landscapeStyle.ts b/web/src/styles/landscapeStyle.ts new file mode 100644 index 000000000..a54121f1f --- /dev/null +++ b/web/src/styles/landscapeStyle.ts @@ -0,0 +1,9 @@ +import { css, DefaultTheme, FlattenInterpolation, ThemeProps } from "styled-components"; + +export const BREAKPOINT_LANDSCAPE = 900; + +export const landscapeStyle = (styleFn: () => FlattenInterpolation>) => css` + @media (min-width: ${BREAKPOINT_LANDSCAPE}px) { + ${() => styleFn()} + } +`; diff --git a/web/src/styles/portraitStyle.ts b/web/src/styles/portraitStyle.ts new file mode 100644 index 000000000..00fd3c062 --- /dev/null +++ b/web/src/styles/portraitStyle.ts @@ -0,0 +1,9 @@ +import { css, DefaultTheme, FlattenInterpolation, ThemeProps } from "styled-components"; + +export const BREAKPOINT_PORTRAIT = 600; + +export const portraitStyle = (styleFn: () => FlattenInterpolation>) => css` + @media (min-width: ${BREAKPOINT_PORTRAIT}px) { + ${() => styleFn()} + } +`; diff --git a/web/src/styles/smallScreenStyle.ts b/web/src/styles/smallScreenStyle.ts deleted file mode 100644 index 10ee1e008..000000000 --- a/web/src/styles/smallScreenStyle.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { css, FlattenSimpleInterpolation } from "styled-components"; - -export const BREAKPOINT_SMALL_SCREEN = 768; - -export const smallScreenStyle = (style: FlattenSimpleInterpolation) => css` - @media (max-width: ${BREAKPOINT_SMALL_SCREEN}px) { - ${style} - } -`; diff --git a/web/src/styles/themes.ts b/web/src/styles/themes.ts index 75a073ffe..096d7ff92 100644 --- a/web/src/styles/themes.ts +++ b/web/src/styles/themes.ts @@ -3,10 +3,13 @@ import { lightTheme as componentsLightTheme, darkTheme as componentsDarkTheme } export const lightTheme = { ...componentsLightTheme, name: "light", + white: "#FFFFFF", primaryPurple: "#4D00B4", secondaryPurple: "#9013FE", mediumPurple: "#F8F1FF", lightPurple: "#FBF9FE", + violetPurple: "#6A1DCD", + lavenderPurple: "#BB72FF", primaryBlue: "#009AFF", secondaryBlue: "#7BCBFF", mediumBlue: "#F0F9FF", @@ -43,10 +46,13 @@ export const lightTheme = { export const darkTheme = { ...componentsDarkTheme, name: "dark", + white: "#FFFFFF", primaryPurple: "#7E1BD4", secondaryPurple: "#B45FFF", mediumPurple: "#390F6C", lightPurple: "#FCFBFF", + violetPurple: "#6A1DCD", + lavenderPurple: "#BB72FF", primaryBlue: "#6CC5FF", secondaryBlue: "#A5DBFF", mediumBlue: "#2E206C", diff --git a/yarn.lock b/yarn.lock index 3447b0119..91b555087 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5288,6 +5288,8 @@ __metadata: graphql-request: ~6.1.0 lru-cache: ^7.18.3 moment: ^2.29.4 + overlayscrollbars: ^2.3.0 + overlayscrollbars-react: ^0.5.2 parcel: 2.8.3 react: ^18.2.0 react-chartjs-2: ^4.3.1 @@ -23990,6 +23992,23 @@ __metadata: languageName: node linkType: hard +"overlayscrollbars-react@npm:^0.5.2": + version: 0.5.2 + resolution: "overlayscrollbars-react@npm:0.5.2" + peerDependencies: + overlayscrollbars: ^2.0.0 + react: ">=16.8.0" + checksum: 64c76ea5510d8b8ff30a0583018797f7a53afe3dd3b3c4c8cdcc960e277c4a5eeeeb2f027f442a65cefdef3850d042dbd8bafb84660cb50d101ad266ebca60fa + languageName: node + linkType: hard + +"overlayscrollbars@npm:^2.3.0": + version: 2.3.0 + resolution: "overlayscrollbars@npm:2.3.0" + checksum: 876dc4f3cfbf2b53335152da465eaa9c6eb64c10dfee319c72b644da9fd7ca2e9094075f34ac5042a99f4d56e5a0146c6c047ec637f478b754cacc43506d211b + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1"
{shortenAddress(sender)}