From caafd1db048159d219d0420ba886ac88c9531b99 Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Wed, 6 Sep 2023 16:11:50 +0300 Subject: [PATCH 1/7] feat(web): cases-grid-and-list-display --- web/src/app.tsx | 23 +++--- web/src/assets/svgs/icons/grid.svg | 3 + web/src/assets/svgs/icons/list.svg | 3 + web/src/components/CasesDisplay/CasesGrid.tsx | 41 ++++++++-- .../CasesDisplay/CasesListHeader.tsx | 79 +++++++++++++++++++ web/src/components/CasesDisplay/Filters.tsx | 32 ++++++++ .../components/DisputeCard/DisputeInfo.tsx | 37 ++++++--- .../components/DisputeCard/PeriodBanner.tsx | 15 ++-- web/src/components/DisputeCard/index.tsx | 72 ++++++++++++++--- web/src/components/Field.tsx | 17 ++-- web/src/context/FilterProvider.tsx | 25 ++++++ web/src/context/Web3Provider.tsx | 2 +- web/src/hooks/queries/useCasesQuery.ts | 10 +-- web/src/hooks/useWindowWidth.ts | 19 +++++ web/src/pages/Cases/index.tsx | 20 ++++- web/src/styles/tabletScreenStyle.ts | 9 +++ 16 files changed, 345 insertions(+), 62 deletions(-) create mode 100644 web/src/assets/svgs/icons/grid.svg create mode 100644 web/src/assets/svgs/icons/list.svg create mode 100644 web/src/components/CasesDisplay/CasesListHeader.tsx create mode 100644 web/src/context/FilterProvider.tsx create mode 100644 web/src/hooks/useWindowWidth.ts create mode 100644 web/src/styles/tabletScreenStyle.ts 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/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..767198a5d --- /dev/null +++ b/web/src/assets/svgs/icons/list.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index b9e9c9415..7f465ff1e 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -1,12 +1,27 @@ import React from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { StandardPagination } from "@kleros/ui-components-library"; +import { smallScreenStyle } from "styles/smallScreenStyle"; +import { useFiltersContext } from "context/FilterProvider"; import { CasesPageQuery } from "queries/useCasesQuery"; import DisputeCard from "components/DisputeCard"; +import CasesListHeader from "./CasesListHeader"; -const Container = styled.div` +const GridContainer = styled.div` + display: grid; + gap: 16px; + grid-template-columns: repeat(3, minmax(50px, 1fr)); + justify-content: center; + align-items: center; + gap: 8px; + ${smallScreenStyle(css` + display: flex; + flex-wrap: wrap; + `)} +`; +const ListContainer = styled.div` display: flex; - flex-wrap: wrap; + flex-direction: column; justify-content: center; gap: 8px; `; @@ -26,13 +41,23 @@ export interface ICasesGrid { } const CasesGrid: React.FC = ({ disputes, currentPage, setCurrentPage, numberDisputes, casesPerPage }) => { + const { isList } = useFiltersContext(); return ( <> - - {disputes.map((dispute, i) => { - return ; - })} - + {!isList ? ( + + {disputes.map((dispute, i) => { + return ; + })} + + ) : ( + + {isList && } + {disputes.map((dispute, i) => { + return ; + })} + + )} theme.secondaryText} !important; + } + + ${smallScreenStyle(css` + display: none; + `)} +`; + +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 ( + + + + + + + + Category + + + + + + + + + ); +}; + +export default CasesListHeader; diff --git a/web/src/components/CasesDisplay/Filters.tsx b/web/src/components/CasesDisplay/Filters.tsx index d74c06574..a4b596cd7 100644 --- a/web/src/components/CasesDisplay/Filters.tsx +++ b/web/src/components/CasesDisplay/Filters.tsx @@ -1,6 +1,9 @@ import React from "react"; import styled, { useTheme } from "styled-components"; import { DropdownSelect } from "@kleros/ui-components-library"; +import { useFiltersContext } from "context/FilterProvider"; +import ListIcon from "svgs/icons/list.svg"; +import GridIcon from "svgs/icons/grid.svg"; const Container = styled.div` display: flex; @@ -9,8 +12,33 @@ const Container = styled.div` width: fit-content; `; +const StyledGridIcon = styled(GridIcon)<{ isList: boolean }>` + cursor: pointer; + transition: fill 0.2s ease; + fill: ${({ theme, isList }) => (isList ? theme.secondaryText : theme.primaryBlue)}; + width: 16px; + height: 16px; +`; + +const IconsContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 4px; +`; + +const StyledListIcon = styled(ListIcon)<{ isList: boolean }>` + cursor: pointer; + transition: fill 0.2s ease; + fill: ${({ theme, isList }) => (isList ? theme.primaryBlue : theme.secondaryText)}; + width: 16px; + height: 17px; +`; + const Filters: React.FC = () => { const theme = useTheme(); + const { isList, setIsList } = useFiltersContext(); + return ( { defaultValue={0} callback={() => {}} /> + + setIsList(false)} /> + setIsList(true)} /> + ); }; diff --git a/web/src/components/DisputeCard/DisputeInfo.tsx b/web/src/components/DisputeCard/DisputeInfo.tsx index 0b3568097..6ea8f9940 100644 --- a/web/src/components/DisputeCard/DisputeInfo.tsx +++ b/web/src/components/DisputeCard/DisputeInfo.tsx @@ -7,10 +7,14 @@ import LawBalanceIcon from "svgs/icons/law-balance.svg"; import PileCoinsIcon from "svgs/icons/pile-coins.svg"; import Field from "../Field"; -const Container = styled.div` +const Container = styled.div<{ isCard: boolean }>` display: flex; - flex-direction: column; - gap: 8px; + flex-direction: ${({ isCard }) => (isCard ? "column" : "row")}; + gap: ${({ isCard }) => (isCard ? "8px" : "48px")}; + justify-content: ${({ isCard }) => (isCard ? "center" : "space-between")}; + align-items: center; + width: 100%; + height: 100%; `; const getPeriodPhrase = (period: Periods): string => { @@ -33,16 +37,31 @@ export interface IDisputeInfo { rewards?: string; period?: Periods; date?: number; + isCard?: boolean; } -const DisputeInfo: React.FC = ({ courtId, court, category, rewards, period, date }) => { +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, isCard = true }) => { return ( - - {category && } - {court && courtId && } - {rewards && } + + {court && courtId && ( + + )} + {category && } + {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 }) => ( - - +const PeriodBanner: React.FC = ({ id, period, isCard = true }) => ( + + {isCard && } ); diff --git a/web/src/components/DisputeCard/index.tsx b/web/src/components/DisputeCard/index.tsx index 2579d0130..8ecc73562 100644 --- a/web/src/components/DisputeCard/index.tsx +++ b/web/src/components/DisputeCard/index.tsx @@ -5,6 +5,7 @@ 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 { CasesPageQuery } from "queries/useCasesQuery"; import { useCourtPolicy } from "queries/useCourtPolicy"; import { useDisputeTemplate } from "queries/useDisputeTemplate"; @@ -18,8 +19,13 @@ const StyledCard = styled(Card)` width: auto; height: 260px; `; +const StyledListItem = styled(Card)` + display: flex; + width: 100%; + height: 64px; +`; -const Container = styled.div` +const CardContainer = styled.div` height: 215px; padding: 24px; display: flex; @@ -29,6 +35,25 @@ const Container = styled.div` margin: 0; } `; +const ListContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 32px; + width: 100%; + margin-right: 2%; + h3 { + margin: 0; + } +`; + +const ListTitle = styled.div` + display: flex; + height: 100%; + justify-content: start; + align-items: center; + min-width: 40vw; +`; export const getPeriodEndTimestamp = ( lastPeriodChange: string, @@ -46,6 +71,7 @@ const DisputeCard: React.FC = ({ lastPeriodChange, court, }) => { + const { isList } = useFiltersContext(); const currentPeriodIndex = Periods[period]; const rewards = `≥ ${formatEther(court.feeForJuror)} ETH`; const date = @@ -63,18 +89,38 @@ const DisputeCard: React.FC = ({ const category = disputeTemplate ? disputeTemplate.category : undefined; const navigate = useNavigate(); return ( - navigate(`/cases/${id.toString()}`)}> - - -

{title}

- -
-
+ <> + {!isList ? ( + navigate(`/cases/${id.toString()}`)}> + + +

{title}

+ +
+
+ ) : ( + navigate(`/cases/${id.toString()}`)}> + + + +

{title}

+
+ +
+
+ )} + ); }; diff --git a/web/src/components/Field.tsx b/web/src/components/Field.tsx index af7b09df8..5c5b92540 100644 --- a/web/src/components/Field.tsx +++ b/web/src/components/Field.tsx @@ -7,9 +7,10 @@ const FieldContainer = styled.div` display: flex; align-items: center; justify-content: flex-start; + white-space: nowrap; .value { flex-grow: 1; - text-align: end; + text-align: ${({ isCard }) => (isCard ? "end" : "center")}; color: ${({ theme }) => theme.primaryText}; } svg { @@ -27,6 +28,7 @@ const FieldContainer = styled.div` type FieldContainerProps = { width?: string; + isCard?: boolean; }; interface IField { @@ -35,12 +37,17 @@ interface IField { value: string; link?: string; width?: string; + isCard?: boolean; } -const Field: React.FC = ({ icon: Icon, name, value, link, width }) => ( - - {} - +const Field: React.FC = ({ icon: Icon, name, value, link, width, isCard = true }) => ( + + {isCard && ( + <> + + + + )} {link ? ( {value} 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/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/useWindowWidth.ts b/web/src/hooks/useWindowWidth.ts new file mode 100644 index 000000000..ff74a14ba --- /dev/null +++ b/web/src/hooks/useWindowWidth.ts @@ -0,0 +1,19 @@ +import { useState, useEffect } from "react"; + +export const useWindowWidth = () => { + const [windowWidth, setWindowWidth] = useState(window.innerWidth); + + useEffect(() => { + const handleResize = () => { + setWindowWidth(window.innerWidth); + }; + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + return windowWidth; +}; diff --git a/web/src/pages/Cases/index.tsx b/web/src/pages/Cases/index.tsx index 86b623fdb..a34ce9a04 100644 --- a/web/src/pages/Cases/index.tsx +++ b/web/src/pages/Cases/index.tsx @@ -1,10 +1,12 @@ -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 { useWindowWidth } from "hooks/useWindowWidth"; +import { BREAKPOINT_TABLET_SCREEN } from "styles/tabletScreenStyle"; 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); @@ -14,8 +16,18 @@ const Container = styled.div` const Cases: React.FC = () => { const [currentPage, setCurrentPage] = useState(1); - const casesPerPage = 3; - const { data } = useCasesQuery(casesPerPage * (currentPage - 1)); + const windowWidth = useWindowWidth(); + const { isList, setIsList } = useFiltersContext(); + const screenIsBig = windowWidth > BREAKPOINT_TABLET_SCREEN; + const casesPerPage = screenIsBig ? 9 : 3; + const { data } = useCasesQuery(casesPerPage * (currentPage - 1), casesPerPage); + + useEffect(() => { + if (!screenIsBig && isList) { + setIsList(false); + } + }, [screenIsBig]); + return ( diff --git a/web/src/styles/tabletScreenStyle.ts b/web/src/styles/tabletScreenStyle.ts new file mode 100644 index 000000000..7b1fe25e7 --- /dev/null +++ b/web/src/styles/tabletScreenStyle.ts @@ -0,0 +1,9 @@ +import { css, DefaultTheme, FlattenInterpolation, ThemeProps } from "styled-components"; + +export const BREAKPOINT_TABLET_SCREEN = 1024; + +export const tabletScreenStyle = (styleFn: () => FlattenInterpolation>) => css` + @media (max-width: ${BREAKPOINT_TABLET_SCREEN}px) { + ${() => styleFn()} + } +`; From f27a98db5c8b0953ab00614b427a9524885b2094 Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Mon, 11 Sep 2023 01:59:57 +0300 Subject: [PATCH 2/7] fix: mobile responsiveness --- web/src/components/CasesDisplay/CasesGrid.tsx | 16 ++++--- .../CasesDisplay/CasesListHeader.tsx | 3 +- .../components/DisputeCard/DisputeInfo.tsx | 26 ++++++----- web/src/components/DisputeCard/index.tsx | 3 +- web/src/components/Field.tsx | 43 ++++++++++--------- web/src/hooks/useWindowWidth.ts | 19 -------- web/src/pages/Cases/index.tsx | 10 +++-- web/src/pages/Dashboard/index.tsx | 4 +- web/src/pages/Home/LatestCases.tsx | 9 +++- web/src/styles/tabletScreenStyle.ts | 6 +-- 10 files changed, 68 insertions(+), 71 deletions(-) delete mode 100644 web/src/hooks/useWindowWidth.ts diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index 7f465ff1e..ad5d96857 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled, { css } from "styled-components"; import { StandardPagination } from "@kleros/ui-components-library"; -import { smallScreenStyle } from "styles/smallScreenStyle"; +import { tabletScreenStyle } from "styles/tabletScreenStyle"; import { useFiltersContext } from "context/FilterProvider"; import { CasesPageQuery } from "queries/useCasesQuery"; import DisputeCard from "components/DisputeCard"; @@ -9,14 +9,18 @@ import CasesListHeader from "./CasesListHeader"; const GridContainer = styled.div` display: grid; - gap: 16px; - grid-template-columns: repeat(3, minmax(50px, 1fr)); - justify-content: center; + row-gap: 16px; + column-gap: 8px; + grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); + justify-content: space-between; align-items: center; - gap: 8px; - ${smallScreenStyle(css` + justify-items: center; + ${tabletScreenStyle(css` display: flex; flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 8px; `)} `; const ListContainer = styled.div` diff --git a/web/src/components/CasesDisplay/CasesListHeader.tsx b/web/src/components/CasesDisplay/CasesListHeader.tsx index ac94d34b9..de51a6717 100644 --- a/web/src/components/CasesDisplay/CasesListHeader.tsx +++ b/web/src/components/CasesDisplay/CasesListHeader.tsx @@ -5,14 +5,15 @@ import WithHelpTooltip from "pages/Dashboard/WithHelpTooltip"; const Container = styled.div` display: flex; - flex-direction: row; justify-content: space-between; gap: 40vw; width: 100%; + height: 100%; `; const CasesData = styled.div` display: flex; + flex-wrap: wrap; align-items: center; width: 100%; gap: 48px; diff --git a/web/src/components/DisputeCard/DisputeInfo.tsx b/web/src/components/DisputeCard/DisputeInfo.tsx index 6ea8f9940..bf8b3c88e 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 { useFiltersContext } from "context/FilterProvider"; import { Periods } from "consts/periods"; import BookmarkIcon from "svgs/icons/bookmark.svg"; import CalendarIcon from "svgs/icons/calendar.svg"; @@ -7,11 +8,11 @@ import LawBalanceIcon from "svgs/icons/law-balance.svg"; import PileCoinsIcon from "svgs/icons/pile-coins.svg"; import Field from "../Field"; -const Container = styled.div<{ isCard: boolean }>` +const Container = styled.div<{ isList: boolean }>` display: flex; - flex-direction: ${({ isCard }) => (isCard ? "column" : "row")}; - gap: ${({ isCard }) => (isCard ? "8px" : "48px")}; - justify-content: ${({ isCard }) => (isCard ? "center" : "space-between")}; + flex-direction: ${({ isList }) => (isList ? "row" : "column")}; + gap: ${({ isList }) => (isList ? "48px" : "8px")}; + justify-content: ${({ isList }) => (isList ? "space-between" : "center")}; align-items: center; width: 100%; height: 100%; @@ -37,7 +38,6 @@ export interface IDisputeInfo { rewards?: string; period?: Periods; date?: number; - isCard?: boolean; } const formatDate = (date: number) => { @@ -47,20 +47,18 @@ const formatDate = (date: number) => { return formattedDate; }; -const DisputeInfo: React.FC = ({ courtId, court, category, rewards, period, date, isCard = true }) => { +const DisputeInfo: React.FC = ({ courtId, court, category, rewards, period, date }) => { + const { isList } = useFiltersContext(); return ( - - {court && courtId && ( - - )} - {category && } - {rewards && } + + {court && courtId && } + {category && } + {rewards && } {typeof period !== "undefined" && date && ( )} diff --git a/web/src/components/DisputeCard/index.tsx b/web/src/components/DisputeCard/index.tsx index 8ecc73562..b1df25e32 100644 --- a/web/src/components/DisputeCard/index.tsx +++ b/web/src/components/DisputeCard/index.tsx @@ -16,7 +16,7 @@ import { isUndefined } from "utils/index"; const StyledCard = styled(Card)` max-width: 380px; min-width: 312px; - width: auto; + width: 380px; height: 260px; `; const StyledListItem = styled(Card)` @@ -111,7 +111,6 @@ const DisputeCard: React.FC = ({

{title}

` width: ${({ width = "100%" }) => width}; @@ -10,7 +11,7 @@ const FieldContainer = styled.div` white-space: nowrap; .value { flex-grow: 1; - text-align: ${({ isCard }) => (isCard ? "end" : "center")}; + text-align: ${({ isList }) => (isList ? "center" : "end")}; color: ${({ theme }) => theme.primaryText}; } svg { @@ -28,7 +29,7 @@ const FieldContainer = styled.div` type FieldContainerProps = { width?: string; - isCard?: boolean; + isList?: boolean; }; interface IField { @@ -37,25 +38,27 @@ interface IField { value: string; link?: string; width?: string; - isCard?: boolean; } -const Field: React.FC = ({ icon: Icon, name, value, link, width, isCard = true }) => ( - - {isCard && ( - <> - - - - )} - {link ? ( - - {value} - - ) : ( - - )} - -); +const Field: React.FC = ({ icon: Icon, name, value, link, width }) => { + const { isList } = useFiltersContext(); + return ( + + {!isList && ( + <> + + + + )} + {link ? ( + + {value} + + ) : ( + + )} + + ); +}; export default Field; diff --git a/web/src/hooks/useWindowWidth.ts b/web/src/hooks/useWindowWidth.ts deleted file mode 100644 index ff74a14ba..000000000 --- a/web/src/hooks/useWindowWidth.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useState, useEffect } from "react"; - -export const useWindowWidth = () => { - const [windowWidth, setWindowWidth] = useState(window.innerWidth); - - useEffect(() => { - const handleResize = () => { - setWindowWidth(window.innerWidth); - }; - - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - - return windowWidth; -}; diff --git a/web/src/pages/Cases/index.tsx b/web/src/pages/Cases/index.tsx index a34ce9a04..ec746e268 100644 --- a/web/src/pages/Cases/index.tsx +++ b/web/src/pages/Cases/index.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import styled from "styled-components"; import { Routes, Route } from "react-router-dom"; import { useCasesQuery } from "queries/useCasesQuery"; -import { useWindowWidth } from "hooks/useWindowWidth"; +import { useWindowSize } from "react-use"; import { BREAKPOINT_TABLET_SCREEN } from "styles/tabletScreenStyle"; import CasesDisplay from "components/CasesDisplay"; import CaseDetails from "./CaseDetails"; @@ -11,14 +11,16 @@ 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 + (64 - 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 windowWidth = useWindowWidth(); + const { width } = useWindowSize(); const { isList, setIsList } = useFiltersContext(); - const screenIsBig = windowWidth > BREAKPOINT_TABLET_SCREEN; + const screenIsBig = width > BREAKPOINT_TABLET_SCREEN; const casesPerPage = screenIsBig ? 9 : 3; const { data } = useCasesQuery(casesPerPage * (currentPage - 1), casesPerPage); diff --git a/web/src/pages/Dashboard/index.tsx b/web/src/pages/Dashboard/index.tsx index aaf615cec..f5d303e4c 100644 --- a/web/src/pages/Dashboard/index.tsx +++ b/web/src/pages/Dashboard/index.tsx @@ -11,7 +11,9 @@ 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 + (64 - 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)` diff --git a/web/src/pages/Home/LatestCases.tsx b/web/src/pages/Home/LatestCases.tsx index d3f9028a6..353e8bece 100644 --- a/web/src/pages/Home/LatestCases.tsx +++ b/web/src/pages/Home/LatestCases.tsx @@ -1,6 +1,7 @@ -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"; @@ -16,6 +17,12 @@ const Container = styled.div` const LatestCases: React.FC = () => { const { data } = useCasesQuery(0); + const { setIsList } = useFiltersContext(); + + useEffect(() => { + setIsList(false); + }, []); + return (

Latest Cases

diff --git a/web/src/styles/tabletScreenStyle.ts b/web/src/styles/tabletScreenStyle.ts index 7b1fe25e7..b047e6603 100644 --- a/web/src/styles/tabletScreenStyle.ts +++ b/web/src/styles/tabletScreenStyle.ts @@ -1,9 +1,9 @@ -import { css, DefaultTheme, FlattenInterpolation, ThemeProps } from "styled-components"; +import { css, FlattenSimpleInterpolation } from "styled-components"; export const BREAKPOINT_TABLET_SCREEN = 1024; -export const tabletScreenStyle = (styleFn: () => FlattenInterpolation>) => css` +export const tabletScreenStyle = (style: FlattenSimpleInterpolation) => css` @media (max-width: ${BREAKPOINT_TABLET_SCREEN}px) { - ${() => styleFn()} + ${style} } `; From 633f50f9537369c095c8c7ca6a18ed759d027544 Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Tue, 12 Sep 2023 04:09:04 +0300 Subject: [PATCH 3/7] fix: responsive layout --- web/src/components/CasesDisplay/CasesGrid.tsx | 4 +- .../CasesDisplay/CasesListHeader.tsx | 47 ++++++++----------- web/src/components/CasesDisplay/Filters.tsx | 17 ++++++- .../components/DisputeCard/DisputeInfo.tsx | 14 ++++-- web/src/components/DisputeCard/index.tsx | 8 ++-- web/src/components/Field.tsx | 4 +- web/src/pages/Cases/CaseDetails/Overview.tsx | 11 ++++- web/src/pages/Cases/index.tsx | 4 +- web/src/pages/Home/Header.tsx | 4 +- web/src/styles/landscapeStyle.ts | 9 ++++ web/src/styles/portraitStyle.ts | 9 ++++ web/src/styles/smallScreenStyle.ts | 9 ---- web/src/styles/tabletScreenStyle.ts | 9 ---- 13 files changed, 87 insertions(+), 62 deletions(-) create mode 100644 web/src/styles/landscapeStyle.ts create mode 100644 web/src/styles/portraitStyle.ts delete mode 100644 web/src/styles/smallScreenStyle.ts delete mode 100644 web/src/styles/tabletScreenStyle.ts diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index ad5d96857..25fbeaf4d 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled, { css } from "styled-components"; import { StandardPagination } from "@kleros/ui-components-library"; -import { tabletScreenStyle } from "styles/tabletScreenStyle"; +import { portraitStyle } from "styles/portraitStyle"; import { useFiltersContext } from "context/FilterProvider"; import { CasesPageQuery } from "queries/useCasesQuery"; import DisputeCard from "components/DisputeCard"; @@ -15,7 +15,7 @@ const GridContainer = styled.div` justify-content: space-between; align-items: center; justify-items: center; - ${tabletScreenStyle(css` + ${portraitStyle(css` display: flex; flex-wrap: wrap; justify-content: center; diff --git a/web/src/components/CasesDisplay/CasesListHeader.tsx b/web/src/components/CasesDisplay/CasesListHeader.tsx index de51a6717..df4be5b0f 100644 --- a/web/src/components/CasesDisplay/CasesListHeader.tsx +++ b/web/src/components/CasesDisplay/CasesListHeader.tsx @@ -1,41 +1,32 @@ import React from "react"; import styled, { css } from "styled-components"; -import { smallScreenStyle } from "styles/smallScreenStyle"; +import { portraitStyle } from "styles/portraitStyle"; import WithHelpTooltip from "pages/Dashboard/WithHelpTooltip"; const Container = styled.div` display: flex; justify-content: space-between; - gap: 40vw; + gap: calc(15vw + (40 - 15) * ((100vw - 300px) / (1250 - 300))); width: 100%; height: 100%; `; const CasesData = styled.div` display: flex; - flex-wrap: wrap; align-items: center; + justify-content: space-around; width: 100%; - gap: 48px; + margin-left: calc(0px + (33) * (100vw - 370px) / (1250 - 370)); + gap: 12px; flex-wrap: wrap; - padding: 0 4%; - justify-content: space-between; - ${smallScreenStyle(css` - margin-left: calc(0px + (33) * (100vw - 370px) / (1250 - 370)); - gap: 12px; + padding: 0 3%; + ${portraitStyle(css` + gap: calc(24px + (48 - 24) * ((100vw - 300px) / (1250 - 300))); `)} `; -const CategoryLabel = styled.label` - margin-left: 32px; -`; - -const RewardsContainer = styled.div` - margin-right: -16px; -`; - const CaseTitle = styled.div` - display: flex; + display: none; margin-left: 32px; gap: 36px; label { @@ -45,11 +36,15 @@ const CaseTitle = styled.div` color: ${({ theme }) => theme.secondaryText} !important; } - ${smallScreenStyle(css` - display: none; + ${portraitStyle(css` + display: flex; `)} `; +const CategoryLabel = styled.label` + padding-left: calc(4px + (12 - 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 " + @@ -64,13 +59,11 @@ const CasesListHeader: React.FC = () => { - - Category - - - - - + Court + + + +
diff --git a/web/src/components/CasesDisplay/Filters.tsx b/web/src/components/CasesDisplay/Filters.tsx index a4b596cd7..894b8487c 100644 --- a/web/src/components/CasesDisplay/Filters.tsx +++ b/web/src/components/CasesDisplay/Filters.tsx @@ -1,7 +1,9 @@ import React from "react"; import styled, { useTheme } 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"; @@ -27,8 +29,9 @@ const IconsContainer = styled.div` gap: 4px; `; -const StyledListIcon = styled(ListIcon)<{ isList: boolean }>` +const StyledListIcon = styled(ListIcon)<{ isList: boolean; isScreenBig: boolean }>` cursor: pointer; + display: ${({ isScreenBig }) => (isScreenBig ? "block" : "none")}; transition: fill 0.2s ease; fill: ${({ theme, isList }) => (isList ? theme.primaryBlue : theme.secondaryText)}; width: 16px; @@ -37,7 +40,9 @@ const StyledListIcon = styled(ListIcon)<{ isList: boolean }>` const Filters: React.FC = () => { const theme = useTheme(); + const { width } = useWindowSize(); const { isList, setIsList } = useFiltersContext(); + const screenIsBig = width > BREAKPOINT_LANDSCAPE; return ( @@ -65,7 +70,15 @@ const Filters: React.FC = () => { /> setIsList(false)} /> - setIsList(true)} /> + { + if (screenIsBig) { + setIsList(true); + } + }} + /> ); diff --git a/web/src/components/DisputeCard/DisputeInfo.tsx b/web/src/components/DisputeCard/DisputeInfo.tsx index bf8b3c88e..07816fec4 100644 --- a/web/src/components/DisputeCard/DisputeInfo.tsx +++ b/web/src/components/DisputeCard/DisputeInfo.tsx @@ -1,5 +1,5 @@ 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"; @@ -11,8 +11,14 @@ import Field from "../Field"; const Container = styled.div<{ isList: boolean }>` display: flex; flex-direction: ${({ isList }) => (isList ? "row" : "column")}; - gap: ${({ isList }) => (isList ? "48px" : "8px")}; - justify-content: ${({ isList }) => (isList ? "space-between" : "center")}; + 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%; @@ -49,10 +55,12 @@ const formatDate = (date: number) => { const DisputeInfo: React.FC = ({ courtId, court, category, rewards, period, date }) => { const { isList } = useFiltersContext(); + return ( {court && courtId && } {category && } + {!category && isList && } {rewards && } {typeof period !== "undefined" && date && ( ` - 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; + flex-grow: ${({ isList }) => (isList ? "0" : "1")}; text-align: ${({ isList }) => (isList ? "center" : "end")}; color: ${({ theme }) => theme.primaryText}; } diff --git a/web/src/pages/Cases/CaseDetails/Overview.tsx b/web/src/pages/Cases/CaseDetails/Overview.tsx index 7de07014f..5e465ce8f 100644 --- a/web/src/pages/Cases/CaseDetails/Overview.tsx +++ b/web/src/pages/Cases/CaseDetails/Overview.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import styled from "styled-components"; import { useParams } from "react-router-dom"; import ReactMarkdown from "react-markdown"; @@ -7,6 +7,7 @@ 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"; @@ -86,10 +87,18 @@ const Overview: React.FC = ({ arbitrable, courtID, currentPeriodIndex const { data: disputeDetails } = useDisputeDetailsQuery(id); const { data: courtPolicyURI } = useCourtPolicyURI(courtID); const { data: courtPolicy } = useCourtPolicy(courtID); + const { isList, setIsList } = useFiltersContext(); const courtName = courtPolicy?.name; const court = disputeDetails?.dispute?.court; const rewards = court ? `≥ ${formatEther(court.feeForJuror)} ETH` : undefined; const category = disputeTemplate ? disputeTemplate.category : undefined; + + useEffect(() => { + if (isList) { + setIsList(false); + } + }, []); + return ( <> diff --git a/web/src/pages/Cases/index.tsx b/web/src/pages/Cases/index.tsx index ec746e268..00e8f7f78 100644 --- a/web/src/pages/Cases/index.tsx +++ b/web/src/pages/Cases/index.tsx @@ -3,7 +3,7 @@ import styled from "styled-components"; import { Routes, Route } from "react-router-dom"; import { useCasesQuery } from "queries/useCasesQuery"; import { useWindowSize } from "react-use"; -import { BREAKPOINT_TABLET_SCREEN } from "styles/tabletScreenStyle"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import CasesDisplay from "components/CasesDisplay"; import CaseDetails from "./CaseDetails"; import { useFiltersContext } from "context/FilterProvider"; @@ -20,7 +20,7 @@ const Cases: React.FC = () => { const [currentPage, setCurrentPage] = useState(1); const { width } = useWindowSize(); const { isList, setIsList } = useFiltersContext(); - const screenIsBig = width > BREAKPOINT_TABLET_SCREEN; + const screenIsBig = width > BREAKPOINT_LANDSCAPE; const casesPerPage = screenIsBig ? 9 : 3; const { data } = useCasesQuery(casesPerPage * (currentPage - 1), casesPerPage); diff --git a/web/src/pages/Home/Header.tsx b/web/src/pages/Home/Header.tsx index 05c825635..17d0e7e96 100644 --- a/web/src/pages/Home/Header.tsx +++ b/web/src/pages/Home/Header.tsx @@ -1,7 +1,7 @@ import React from "react"; import { useTheme } from "styled-components"; import { useMeasure } from "react-use"; -import { BREAKPOINT_SMALL_SCREEN } from "styles/smallScreenStyle"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; 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"; @@ -11,7 +11,7 @@ const Header = () => { const [ref, { width }] = useMeasure(); const theme = useTheme(); const themeIsLight = theme.name === "light"; - const screenIsBig = width > BREAKPOINT_SMALL_SCREEN; + const screenIsBig = width > BREAKPOINT_LANDSCAPE; return (
{screenIsBig ? : } diff --git a/web/src/styles/landscapeStyle.ts b/web/src/styles/landscapeStyle.ts new file mode 100644 index 000000000..69205ca07 --- /dev/null +++ b/web/src/styles/landscapeStyle.ts @@ -0,0 +1,9 @@ +import { css, FlattenSimpleInterpolation } from "styled-components"; + +export const BREAKPOINT_LANDSCAPE = 900; + +export const landscapeStyle = (style: FlattenSimpleInterpolation) => css` + @media (min-width: ${BREAKPOINT_LANDSCAPE}px) { + ${style} + } +`; diff --git a/web/src/styles/portraitStyle.ts b/web/src/styles/portraitStyle.ts new file mode 100644 index 000000000..540bb3d96 --- /dev/null +++ b/web/src/styles/portraitStyle.ts @@ -0,0 +1,9 @@ +import { css, FlattenSimpleInterpolation } from "styled-components"; + +export const BREAKPOINT_PORTRAIT = 600; + +export const portraitStyle = (style: FlattenSimpleInterpolation) => css` + @media (min-width: ${BREAKPOINT_PORTRAIT}px) { + ${style} + } +`; 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/tabletScreenStyle.ts b/web/src/styles/tabletScreenStyle.ts deleted file mode 100644 index b047e6603..000000000 --- a/web/src/styles/tabletScreenStyle.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { css, FlattenSimpleInterpolation } from "styled-components"; - -export const BREAKPOINT_TABLET_SCREEN = 1024; - -export const tabletScreenStyle = (style: FlattenSimpleInterpolation) => css` - @media (max-width: ${BREAKPOINT_TABLET_SCREEN}px) { - ${style} - } -`; From dff0822709cde19c50e99298ab0533f6286d4ede Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Tue, 12 Sep 2023 04:19:15 +0300 Subject: [PATCH 4/7] fix: dashboard list view --- web/src/components/CasesDisplay/CasesGrid.tsx | 4 ++-- .../components/CasesDisplay/CasesListHeader.tsx | 6 +++--- web/src/pages/Dashboard/index.tsx | 14 +++++++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index 25fbeaf4d..bbb290777 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled, { css } from "styled-components"; import { StandardPagination } from "@kleros/ui-components-library"; -import { portraitStyle } from "styles/portraitStyle"; +import { landscapeStyle } from "styles/landscapeStyle"; import { useFiltersContext } from "context/FilterProvider"; import { CasesPageQuery } from "queries/useCasesQuery"; import DisputeCard from "components/DisputeCard"; @@ -15,7 +15,7 @@ const GridContainer = styled.div` justify-content: space-between; align-items: center; justify-items: center; - ${portraitStyle(css` + ${landscapeStyle(css` display: flex; flex-wrap: wrap; justify-content: center; diff --git a/web/src/components/CasesDisplay/CasesListHeader.tsx b/web/src/components/CasesDisplay/CasesListHeader.tsx index df4be5b0f..845c66ee9 100644 --- a/web/src/components/CasesDisplay/CasesListHeader.tsx +++ b/web/src/components/CasesDisplay/CasesListHeader.tsx @@ -1,6 +1,6 @@ import React from "react"; import styled, { css } from "styled-components"; -import { portraitStyle } from "styles/portraitStyle"; +import { landscapeStyle } from "styles/landscapeStyle"; import WithHelpTooltip from "pages/Dashboard/WithHelpTooltip"; const Container = styled.div` @@ -20,7 +20,7 @@ const CasesData = styled.div` gap: 12px; flex-wrap: wrap; padding: 0 3%; - ${portraitStyle(css` + ${landscapeStyle(css` gap: calc(24px + (48 - 24) * ((100vw - 300px) / (1250 - 300))); `)} `; @@ -36,7 +36,7 @@ const CaseTitle = styled.div` color: ${({ theme }) => theme.secondaryText} !important; } - ${portraitStyle(css` + ${landscapeStyle(css` display: flex; `)} `; diff --git a/web/src/pages/Dashboard/index.tsx b/web/src/pages/Dashboard/index.tsx index f5d303e4c..918a179df 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"; @@ -30,10 +33,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]); + return ( {isConnected ? ( From d233f690769f6e01b0e57b7ebfab1c953583a7de Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Tue, 12 Sep 2023 04:21:32 +0300 Subject: [PATCH 5/7] fix: code smell --- web/src/components/CasesDisplay/CasesGrid.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index bbb290777..6bf6cf5e6 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -50,15 +50,15 @@ const CasesGrid: React.FC = ({ disputes, currentPage, setCurrentPage <> {!isList ? ( - {disputes.map((dispute, i) => { - return ; + {disputes.map((dispute) => { + return ; })} ) : ( {isList && } - {disputes.map((dispute, i) => { - return ; + {disputes.map((dispute) => { + return ; })} )} From 673c60a6db9383e5e1ffef11897a2ecbd77f7d85 Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Fri, 15 Sep 2023 14:11:40 +0300 Subject: [PATCH 6/7] fix: responsive layout adjustments --- web/src/assets/svgs/icons/list.svg | 2 +- web/src/components/CasesDisplay/CasesGrid.tsx | 20 +++++++++--------- .../CasesDisplay/CasesListHeader.tsx | 13 +++++------- web/src/components/CasesDisplay/Filters.tsx | 20 ++++++++++++------ web/src/components/DisputeCard/index.tsx | 21 +++++++++++++------ web/src/pages/Cases/index.tsx | 13 +++++++----- web/src/pages/Dashboard/index.tsx | 13 +++++++----- 7 files changed, 61 insertions(+), 41 deletions(-) diff --git a/web/src/assets/svgs/icons/list.svg b/web/src/assets/svgs/icons/list.svg index 767198a5d..338c5b4a4 100644 --- a/web/src/assets/svgs/icons/list.svg +++ b/web/src/assets/svgs/icons/list.svg @@ -1,3 +1,3 @@ - + diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index 6bf6cf5e6..7aca520dc 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -8,19 +8,19 @@ import DisputeCard from "components/DisputeCard"; import CasesListHeader from "./CasesListHeader"; const GridContainer = styled.div` - display: grid; - row-gap: 16px; - column-gap: 8px; - grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); - justify-content: space-between; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 8px; align-items: center; justify-items: center; ${landscapeStyle(css` - display: flex; - flex-wrap: wrap; - justify-content: center; - align-items: center; - gap: 8px; + 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` diff --git a/web/src/components/CasesDisplay/CasesListHeader.tsx b/web/src/components/CasesDisplay/CasesListHeader.tsx index 845c66ee9..e7ee8bb8c 100644 --- a/web/src/components/CasesDisplay/CasesListHeader.tsx +++ b/web/src/components/CasesDisplay/CasesListHeader.tsx @@ -17,12 +17,9 @@ const CasesData = styled.div` justify-content: space-around; width: 100%; margin-left: calc(0px + (33) * (100vw - 370px) / (1250 - 370)); - gap: 12px; flex-wrap: wrap; padding: 0 3%; - ${landscapeStyle(css` - gap: calc(24px + (48 - 24) * ((100vw - 300px) / (1250 - 300))); - `)} + gap: calc(24px + (48 - 24) * ((100vw - 300px) / (1250 - 300))); `; const CaseTitle = styled.div` @@ -41,8 +38,8 @@ const CaseTitle = styled.div` `)} `; -const CategoryLabel = styled.label` - padding-left: calc(4px + (12 - 4) * ((100vw - 300px) / (900 - 300))); +const StyledLabel = styled.label` + padding-left: calc(4px + (8 - 4) * ((100vw - 300px) / (900 - 300))); `; const tooltipMsg = @@ -59,8 +56,8 @@ const CasesListHeader: React.FC = () => { - Court - + Court + Category diff --git a/web/src/components/CasesDisplay/Filters.tsx b/web/src/components/CasesDisplay/Filters.tsx index 894b8487c..26f706793 100644 --- a/web/src/components/CasesDisplay/Filters.tsx +++ b/web/src/components/CasesDisplay/Filters.tsx @@ -1,5 +1,5 @@ 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"; @@ -14,12 +14,18 @@ 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: fill 0.2s ease; - fill: ${({ theme, isList }) => (isList ? theme.secondaryText : theme.primaryBlue)}; + transition: filter 0.2s ease; + fill: ${({ theme }) => theme.primaryBlue}; width: 16px; height: 16px; + overflow: hidden; + ${({ isList }) => !isList && glowingEffect} `; const IconsContainer = styled.div` @@ -32,10 +38,12 @@ const IconsContainer = styled.div` const StyledListIcon = styled(ListIcon)<{ isList: boolean; isScreenBig: boolean }>` cursor: pointer; display: ${({ isScreenBig }) => (isScreenBig ? "block" : "none")}; - transition: fill 0.2s ease; - fill: ${({ theme, isList }) => (isList ? theme.primaryBlue : theme.secondaryText)}; + transition: filter 0.2s ease; + fill: ${({ theme }) => theme.primaryBlue}; width: 16px; - height: 17px; + height: 16px; + overflow: hidden; + ${({ isList }) => isList && glowingEffect} `; const Filters: React.FC = () => { diff --git a/web/src/components/DisputeCard/index.tsx b/web/src/components/DisputeCard/index.tsx index a4c2156c2..29e4da791 100644 --- a/web/src/components/DisputeCard/index.tsx +++ b/web/src/components/DisputeCard/index.tsx @@ -1,11 +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,10 +15,11 @@ import PeriodBanner from "./PeriodBanner"; import { isUndefined } from "utils/index"; const StyledCard = styled(Card)` - max-width: 380px; - min-width: 312px; - width: 380px; + width: 312px; height: 260px; + ${landscapeStyle(css` + width: 380px; + `)} `; const StyledListItem = styled(Card)` display: flex; @@ -40,7 +42,6 @@ const ListContainer = styled.div` display: flex; justify-content: space-between; align-items: flex-start; - /* gap: calc(8px + (32 - 8) * ((100vw - 300px) / (900 - 300))); */ width: 100%; margin-right: 8px; @@ -66,6 +67,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, @@ -110,7 +116,10 @@ const DisputeCard: React.FC = ({ -

{title}

+
theme.lightBackground}; - padding: calc(32px + (132 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); - padding-top: calc(32px + (64 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); - padding-bottom: calc(64px + (96 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding: 32px; + ${landscapeStyle(css` + padding: calc(32px + (132 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-top: calc(32px + (64 - 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 = () => { diff --git a/web/src/pages/Dashboard/index.tsx b/web/src/pages/Dashboard/index.tsx index 918a179df..3ff458195 100644 --- a/web/src/pages/Dashboard/index.tsx +++ b/web/src/pages/Dashboard/index.tsx @@ -1,10 +1,10 @@ import React, { useState, useEffect } from "react"; -import styled from "styled-components"; +import styled, { css } 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 { landscapeStyle, BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import JurorInfo from "./JurorInfo"; import Courts from "./Courts"; import CasesDisplay from "components/CasesDisplay"; @@ -14,9 +14,12 @@ const Container = styled.div` width: 100%; min-height: calc(100vh - 144px); background-color: ${({ theme }) => theme.lightBackground}; - padding: calc(32px + (132 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); - padding-top: calc(32px + (64 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); - padding-bottom: calc(64px + (96 - 64) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding: 32px; + ${landscapeStyle(css` + padding: calc(32px + (132 - 32) * (min(max(100vw, 375px), 1250px) - 375px) / 875); + padding-top: calc(32px + (64 - 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)` From d3a54c7f3d2fb302dad8cab973f23ff655b93733 Mon Sep 17 00:00:00 2001 From: nhestrompia Date: Fri, 15 Sep 2023 14:45:49 +0300 Subject: [PATCH 7/7] fix: dashboard layout --- web/src/components/CasesDisplay/CasesGrid.tsx | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index 7aca520dc..13cf918d3 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -6,22 +6,26 @@ 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 GridContainer = styled.div` +const GridContainer = styled.div<{ path: string }>` display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 8px; - align-items: center; - justify-items: center; - ${landscapeStyle(css` - display: grid; - row-gap: 16px; - column-gap: 8px; - grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); - justify-content: space-between; - `)} + ${(props) => + props.path === "/dashboard" + ? landscapeStyle(css` + display: flex; + `) + : landscapeStyle(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; @@ -46,10 +50,13 @@ export interface ICasesGrid { const CasesGrid: React.FC = ({ disputes, currentPage, setCurrentPage, numberDisputes, casesPerPage }) => { const { isList } = useFiltersContext(); + const location = useLocation(); + + const path = location.pathname; return ( <> {!isList ? ( - + {disputes.map((dispute) => { return ; })}