diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index eca783cbf9..d587de765a 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -74,7 +74,7 @@ const DropdownMenuItem = styled(MenuItem)` const ToolbarHeader = styled(Toolbar)` backdrop-filter: blur(16px); - background-color: rgba(19, 111, 149, 37%) !important; + background-color: ${constants.colorHeaderToolbar}; box-shadow: 2px 2px 3px -2px rgb(0 0 0 / 23%); width: 100%; z-index: 200; diff --git a/src/components/Heading/Styled.tsx b/src/components/Heading/Styled.tsx index 9518463f01..d6e0207f29 100644 --- a/src/components/Heading/Styled.tsx +++ b/src/components/Heading/Styled.tsx @@ -18,7 +18,7 @@ export const StyledDiv = styled.div` align-items: baseline; letter-spacing: 10px; font-weight: bold; - background-color: rgba(14, 84, 113, 37%); + background-color: ${constants.colorHeaderSection}; font-family: ${constants.fontFamilyFuturistic}; } diff --git a/src/components/Match/MatchHeader/MatchHeader.tsx b/src/components/Match/MatchHeader/MatchHeader.tsx index 34f1ae1562..376d8bb940 100644 --- a/src/components/Match/MatchHeader/MatchHeader.tsx +++ b/src/components/Match/MatchHeader/MatchHeader.tsx @@ -20,7 +20,7 @@ const Styled = styled.header` left: 50%; right: 50%; padding-top: 35px; - background-color: rgba(14, 84, 113, 37%); + background-color: ${constants.colorHeaderSection}; .matchInfo { display: grid; diff --git a/src/components/Player/Header/PlayerButtons.tsx b/src/components/Player/Header/PlayerButtons.tsx index d27558be98..3bc756d265 100644 --- a/src/components/Player/Header/PlayerButtons.tsx +++ b/src/components/Player/Header/PlayerButtons.tsx @@ -54,7 +54,7 @@ class PlayerButtons extends React.Component<
diff --git a/src/components/Player/Header/PlayerHeader.tsx b/src/components/Player/Header/PlayerHeader.tsx index 38217f12b2..d92a7bdc43 100644 --- a/src/components/Player/Header/PlayerHeader.tsx +++ b/src/components/Player/Header/PlayerHeader.tsx @@ -19,7 +19,7 @@ const Styled = styled.div` right: 50%; display: grid; padding-top: 35px; - background-color: rgba(14, 84, 113, 37%); + background-color: ${constants.colorHeaderSection}; grid-template-columns: 1fr minmax(min-content, ${constants.appWidth}px) 1fr; .container { diff --git a/src/components/Player/Pages/Overview/CountsSummary.tsx b/src/components/Player/Pages/Overview/CountsSummary.tsx index b68a14d84f..fb5e07a794 100644 --- a/src/components/Player/Pages/Overview/CountsSummary.tsx +++ b/src/components/Player/Pages/Overview/CountsSummary.tsx @@ -5,16 +5,13 @@ import GaugeChart from "./../../../Visualizations/GaugeChart"; import constants from "../../../constants"; const Styled = styled.div` + position: relative; border: 1px solid rgb(0, 0, 0, 0.12); background-color: rgba(255, 255, 255, 0.03); overflow: hidden; - position: relative; - - .gauge-container { - justify-content: center; - display: flex; - flex-wrap: wrap; - } + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; @media only screen and (min-width: ${constants.appWidth}px) { .gauge-chart:nth-child(even)::after { @@ -28,18 +25,44 @@ const Styled = styled.div` } } `; +const Group = styled.div` + padding-bottom: 8px; + display: flex; + flex-direction: column; + align-items: center; +`; +const GroupLabel = styled.div` + padding: 8px 0; + font-family: ${constants.fontFamilyFuturistic}; + font-size: ${constants.fontSizeSmall}; + text-align: center; +`; +const ChartPair = styled.div` + display: flex; + flex-direction: row; +`; -const Summary = ({ data }: { data: any[] }) => ( +const Summary = ({ + data, +}: { + data: Record; +}) => ( -
- {data.map((el) => ( - - ))} -
+ {Object.values(data).flatMap((group) => ( + + {group.label} + + {group.data.map((el: any) => ( + + ))} + + + ))}
); diff --git a/src/components/Player/Pages/Overview/Overview.tsx b/src/components/Player/Pages/Overview/Overview.tsx index d5bd0ad795..0f7f8ddee6 100644 --- a/src/components/Player/Pages/Overview/Overview.tsx +++ b/src/components/Player/Pages/Overview/Overview.tsx @@ -36,6 +36,7 @@ const SummaryContainer = styled(Container)` & ul { border: 1px solid rgb(0, 0, 0, 0.12); + border-radius: 2px; background-color: rgba(255, 255, 255, 0.03); margin: 0; padding-left: 5px; @@ -112,7 +113,7 @@ type OverviewProps = { peersData: any[]; peersLoading: boolean; peersError: string; - countsData: any[]; + countsData: Record; countsLoading: boolean; countsError: string; location: { @@ -160,7 +161,7 @@ const Overview = ({ @@ -370,13 +371,28 @@ const filterCounts = (counts: Record) => { } }); - return [ - ...countMap.is_radiant, - ...countMap.game_mode, - ...countMap.region, - ...countMap.lane_role, - ...countMap.patch, - ]; + return { + game_mode: { + label: "Game Mode", + data: countMap.game_mode, + }, + is_radiant: { + label: "Team", + data: countMap.is_radiant, + }, + region: { + label: "Region", + data: countMap.region, + }, + lane_role: { + label: "Role", + data: countMap.lane_role, + }, + patch: { + label: "Patch", + data: countMap.patch, + }, + }; }; const mapStateToProps = (state: any) => ({ diff --git a/src/components/Player/Pages/Overview/Summary.tsx b/src/components/Player/Pages/Overview/Summary.tsx index 12ba668bc6..153b2bc83e 100644 --- a/src/components/Player/Pages/Overview/Summary.tsx +++ b/src/components/Player/Pages/Overview/Summary.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Link } from "react-router-dom"; import { heroes } from "dotaconstants"; +import styled from "styled-components"; import { isRadiant, sum, @@ -12,6 +13,37 @@ import constants from "../../../constants"; import HeroImage from "../../../Visualizations/HeroImage"; import useStrings from "../../../../hooks/useStrings.hook"; +const BulletList = styled.ul` + display: flex; + align-items: flex-start; + flex-wrap: wrap; + justify-content: space-evenly; + gap: 0.5rem; +`; +const StyledBulletListItem = styled.li` + margin: 0 !important; + padding: 8px; + font-family: ${constants.fontFamilyFuturistic}; +`; +const Label = styled.span` + font-size: 0.7rem; +`; +const StyledLink = styled(Link)<{ color?: string }>` + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + color: ${(props) => props.color}; + &:hover { + filter: brightness(1.6); + } +`; +const StatList = styled.div` + display: flex; + align-items: center; + gap: 4px; +`; + const SummOfRecMatches = ({ matchesData }: { matchesData: any[] }) => { const strings = useStrings(); // initial values @@ -62,7 +94,7 @@ const SummOfRecMatches = ({ matchesData }: { matchesData: any[] }) => { color = "red"; break; case "assists": - color = "lightGray"; + color = "colorNeutralTier3"; break; case "gold_per_min": color = "golden"; @@ -91,52 +123,51 @@ const SummOfRecMatches = ({ matchesData }: { matchesData: any[] }) => { return (
-
    + {winrate ? ( -
  • - {strings.th_winrate} + +

    {winrate}%

    -
  • - ) : null}{" "} + + ) : null} {Object.keys(computed).map((key) => { const c = computed[key]; if (c.avg) { const hero = heroes[c.max.heroId as keyof Heroes] || {}; return ( -
  • - {strings[`heading_${key}` as keyof Strings]} - -

    + + + + {key === "duration" ? formatSeconds(c.avg) : abbreviateNumber(c.avg)} -   - - {key === "duration" - ? formatSeconds(c.max.value) - : abbreviateNumber(c.max.value)} - - -

    - -
  • + + + {key === "duration" + ? formatSeconds(c.max.value) + : abbreviateNumber(c.max.value)} + + + + ); } - return null; })} -
+
); }; diff --git a/src/components/TabBar/TabBar.tsx b/src/components/TabBar/TabBar.tsx index 146df2daf5..dab8d5760f 100644 --- a/src/components/TabBar/TabBar.tsx +++ b/src/components/TabBar/TabBar.tsx @@ -8,7 +8,7 @@ import { withRouter } from "react-router-dom"; const StyledMain = styled.main` position: relative; margin: 0px 0px 30px 0px; - background-color: rgba(14, 84, 113, 37%); + background-color: ${constants.colorHeaderSection}; border-bottom: 1px solid rgba(255, 255, 255, 0.1); width: 100vw; left: 50%; diff --git a/src/components/Visualizations/GaugeChart.tsx b/src/components/Visualizations/GaugeChart.tsx index 011d28fd43..9d1d68fc56 100644 --- a/src/components/Visualizations/GaugeChart.tsx +++ b/src/components/Visualizations/GaugeChart.tsx @@ -1,6 +1,7 @@ import React from "react"; import styled from "styled-components"; import { abbreviateNumber } from "../../utility"; +import constants from "../constants"; const Styled = styled.div<{ percent?: number; meterColor?: string }>` font-size: 60%; @@ -69,78 +70,89 @@ const Styled = styled.div<{ percent?: number; meterColor?: string }>` } .percentage-indicator { - font: bold 1.25em/1.6 sans-serif; + font-family: ${constants.fontFamilyFuturistic}; + font-size: 0.6rem; + font-weight: 600; + letter-spacing: 0.04em; + line-height: 1.75; color: ${(props) => props.meterColor}; - - white-space: pre; - vertical-align: baseline; - user-select: none; + font-variant-numeric: tabular-nums; text-align: center; } .caption { - position: relative; + color: #9ca3af; + font-size: 9px; + font-weight: 500; + letter-spacing: 0.12em; text-align: center; - color: rgb(179, 179, 179); - font-size: 12px; - bottom: 5px; text-transform: uppercase; - width: 96px; - max-height: 16px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - margin-top: 2px; + padding-bottom: 5px; } .win-number { + width: 24px; position: absolute; left: 0px; + font-family: ${constants.fontFamilyFuturistic}; + font-size: 0.95em; + font-weight: 500; + font-variant-numeric: tabular-nums; + color: #a8adb8; text-align: center; - color: rgb(144, 144, 144); - font-family: Tahoma; - width: 24px; ::before { + content: "W"; position: absolute; bottom: 1.8em; left: 8px; - content: "W"; - color: white; - text-shadow: 1px 1px black; + font-family: ${constants.fontFamilyFuturistic}; + color: ${constants.colorWhite}; + text-shadow: none; } } .loss-number { + width: 24px; position: absolute; right: 0px; + font-family: ${constants.fontFamilyFuturistic}; + font-size: 0.95em; + font-weight: 500; + font-variant-numeric: tabular-nums; + color: #a8adb8; text-align: center; - color: rgb(144, 144, 144); - font-family: Tahoma; - width: 24px; ::before { + content: "L"; position: absolute; bottom: 1.8em; right: 8px; - content: "L"; - color: white; - text-shadow: 1px 1px black; + font-family: ${constants.fontFamilyFuturistic}; + color: ${constants.colorWhite}; + text-shadow: none; } } + + .total-matches { + font-family: ${constants.fontFamilyFuturistic}; + font-size: 9px; + color: ${constants.colorGreyMuted}; + text-align: center; + } `; const computeMeterPercent = (value: number) => 0.005 * value; const computeMeterColor = (value: number) => { - if (value < 45) { - return "rgb(179,132,91)"; - } else if (value < 50) { - return "rgb(156,148,96)"; - } else if (value < 53) { - return "rgb(140,159,99)"; + if (value < 20) { + return "#1769AA"; + } else if (value < 40) { + return "#0078D4"; + } else if (value < 80) { + return "#149BD7"; } - return "rgb(117,176,103)"; + return "#50E6FF"; }; const GaugeChart = ({ @@ -172,6 +184,7 @@ const GaugeChart = ({
{abbreviateNumber(Math.round((number / 100) * (100 - percent)))}
+
{number}
); diff --git a/src/components/Visualizations/Graph/HistogramGraph.tsx b/src/components/Visualizations/Graph/HistogramGraph.tsx index 679a0fb162..9b1d75c089 100644 --- a/src/components/Visualizations/Graph/HistogramGraph.tsx +++ b/src/components/Visualizations/Graph/HistogramGraph.tsx @@ -9,7 +9,7 @@ import { XAxis, YAxis, } from "recharts"; -import { formatGraphValueData, percentile } from "../../../utility"; +import { formatGraphValueData, getPercentileColor } from "../../../utility"; import { StyledTooltip, TooltipLabel, HistogramTooltipDiv } from "./Styled"; import Grid from "./Grid"; import useStrings from "../../../hooks/useStrings.hook"; @@ -30,10 +30,9 @@ const HistogramTooltipContent = ({ const data = payload && payload[0] && payload[0].payload; const winratePercentile = data && data?.games > 0 && data?.win / data?.games; const winratePercent = formatPercent(winratePercentile); - const grade = percentile(winratePercentile); const numWins = data?.win; const numLosses = data?.games - data?.win; - const color = String(constants[grade.color as keyof typeof constants]); + const color = getPercentileColor(winratePercentile); return ( @@ -115,14 +114,8 @@ const HistogramGraph = ({ {columns.map((entry) => { const { win, games, x } = entry; const percent = win / games; - const grade = percentile(percent); - const stroke = String( - constants[grade.color as keyof typeof constants], - ); - const color = String( - constants[grade.color as keyof typeof constants], - ); - return ; + const color = getPercentileColor(percent); + return ; })} diff --git a/src/components/constants.ts b/src/components/constants.ts index bb1db85db3..faeaddf79e 100644 --- a/src/components/constants.ts +++ b/src/components/constants.ts @@ -68,6 +68,8 @@ export default { colorBoxBlue: "rgba(71, 114, 179, 0.05)", colorGraphYellow: "rgb(223,217,94)", colorGraphBlue: "rgb(192,217,220)", + colorHeaderSection: "rgba(14, 84, 113, 37%)", + colorHeaderToolbar: "rgba(19, 111, 149, 37%)", sliderTicksColor: "#757575", sliderTicksColorActive: "#337AB7", dividerColor: "rgb(52, 50, 50)", diff --git a/src/index.css b/src/index.css index c1ac92aa0a..db2f70a179 100644 --- a/src/index.css +++ b/src/index.css @@ -25,10 +25,13 @@ li { list-style-type: none; } +:root { + --base-background: var(--opendota-blue); + --opendota-blue: #171f37; +} + #root { - background-color: #192023; - background-image: -webkit-linear-gradient(to right, #1a2b3e, #141e30); - background-image: linear-gradient(to right, #1a2b3e, #141e30); + background: var(--base-background); color: rgba(255, 255, 255, 0.87); height: 100%; min-height: 100vh; diff --git a/src/utility.tsx b/src/utility.tsx index 81937eb068..358593b261 100644 --- a/src/utility.tsx +++ b/src/utility.tsx @@ -145,6 +145,12 @@ export const jsonFn = (json: any) => (arrayFn: any) => (fn: any) => //@ts-expect-error json[Object.keys(json)[arrayFn]((key, index) => fn(json[key], index))]; +/** + * Get percentile grade data for a proportion. + * + * @param pct Percent should be between 0 and 1.0. + * @returns Object containing `color` and `grade` for the percentile. + */ export const percentile = (pct: number) => { if (pct >= 0.8) { return { @@ -173,6 +179,18 @@ export const percentile = (pct: number) => { }; }; +/** + * Get color based on percentile. + * + * @param percent Percent should be between 0 and 1.0. + * @returns Color string for the percentile grade. + */ +export const getPercentileColor = (percent: number) => { + const grade = percentile(percent); + const color = String(constants[grade.color as keyof typeof constants]); + return color; +}; + export const IMAGESIZE_ENUM = { SMALL: { // ~10KB