diff --git a/front-end/src/apps/audience-display/displays/ad-default.tsx b/front-end/src/apps/audience-display/displays/ad-default.tsx index 99e7bf1e..a47562e5 100644 --- a/front-end/src/apps/audience-display/displays/ad-default.tsx +++ b/front-end/src/apps/audience-display/displays/ad-default.tsx @@ -5,6 +5,8 @@ import { matchOccurringAtom, matchOccurringRanksAtom } from 'src/stores/recoil'; import { useEvent } from 'src/api/use-event-data'; import { Displays } from '@toa-lib/models'; import { getDisplays } from './displays'; +import { FadeInOut, SlideInBottom } from 'src/components/animations'; +import AbsolouteLocator from 'src/components/util/absoloute-locator'; /** * Classic audience display that handles all scenarios. @@ -18,19 +20,34 @@ export const AudDisplayDefault: FC = ({ id }) => { const displays = getDisplays(event?.seasonKey || ''); // TODO - Have better error handling here. if (!match || !event || !ranks || !displays) return null; - switch (id) { - case Displays.BLANK: - case Displays.MATCH_PREVIEW: - return ( - - ); - case Displays.MATCH_START: - return ; - case Displays.MATCH_RESULTS: - return ( - - ); - default: - return null; - } + + return ( + <> + {/* Displays.BLANK (show nothing) */} + {id === Displays.BLANK && <>} + + {/* Displays.MATCH_PREVIEW */} + + + + + + + {/* Displays.MATCH_START */} + + + + + {/* Displays.MATCH_RESULTS */} + + + + + + + ); }; diff --git a/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/alliance-play.tsx b/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/alliance-play.tsx index b899be4e..bd21290f 100644 --- a/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/alliance-play.tsx +++ b/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/alliance-play.tsx @@ -82,7 +82,7 @@ export const AlliancePlay: FC = ({ alliance, participants, invert }) => { } : { borderTopLeftRadius: '0.5em', - borderBottomLeftRadius: '0.em', + borderBottomLeftRadius: '0.5em', margin: '0.5em 0 0.5em 0.5em' } } diff --git a/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/match-title.tsx b/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/match-title.tsx new file mode 100644 index 00000000..8da63329 --- /dev/null +++ b/front-end/src/apps/audience-display/displays/seasons/fgc_default/components/match-title.tsx @@ -0,0 +1,31 @@ +import styled from '@emotion/styled'; +import { Match } from '@toa-lib/models'; + +const InfoContainer = styled.div` + grid-area: info; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #ffffff; + border-radius: 0.5em; + margin-left: 15%; + margin-right: 15%; + padding-top: .1em; + padding-bottom: .1em; + color: black; + line-height: 0.95; + font-size: 4vh; + font-weight: 800; +`; + +const MatchTitle = ({ match }: { match: Match }) => { + return ( + +
{match.name}
+
Field {match.fieldNumber}
+
+ ); +}; + +export default MatchTitle; diff --git a/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-play.tsx b/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-play.tsx index 34a61102..a0216a4c 100644 --- a/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-play.tsx +++ b/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-play.tsx @@ -6,7 +6,7 @@ import { MatchScoreBug } from './components/match-score-bug'; const Container = styled.div` background-color: #000000; position: absolute; - bottom: 0.5vh; + bottom: 0; left: 32vw; height: 18vh; width: 36vw; diff --git a/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-preview.tsx b/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-preview.tsx index d17d4529..e8ccc7ad 100644 --- a/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-preview.tsx +++ b/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-preview.tsx @@ -3,17 +3,31 @@ import { DisplayProps } from '../../displays'; import styled from '@emotion/styled'; import FGC_BG from './assets/global-bg.png'; import FGC_LOGO from './assets/fg-logo-lg.png'; -import { InfoBar } from './components/info-bar'; import { AlliancePreview } from './components/alliance-preview'; +import MatchTitle from './components/match-title'; -const Container = styled.div` +const BGImage = styled.div` background-image: url(${FGC_BG}); background-size: cover; width: 100vw; height: 100vh; +`; + +const Container = styled.div` + background-size: cover; + background: linear-gradient( + to right, + #00000000, + #00000000 15%, + #0000007c 15%, + #0000007c 85%, + #00000000 85% + ); + width: 100vw; + height: 100vh; overflow: hidden; display: grid; - grid-template-rows: 20vh 7vh 30vh 30vh; + grid-template-rows: 20vh 9vh 30vh 30vh; grid-template-areas: 'logo' 'info' @@ -35,35 +49,25 @@ const Logo = styled.img` width: auto; `; -const InfoContainer = styled.div` - grid-area: info; - display: flex; - justify-content: space-evenly; - align-items: center; -`; - export const MatchPreview: FC = ({ match, ranks }) => { - const matchParts = match.name.split(' '); - const matchNumber = matchParts[matchParts.length - 1]; return ( - - - - - - - - - - - + + + + + + + + + + ); }; diff --git a/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-results.tsx b/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-results.tsx index 91a85a32..e7ec3850 100644 --- a/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-results.tsx +++ b/front-end/src/apps/audience-display/displays/seasons/fgc_default/match-results.tsx @@ -3,6 +3,7 @@ import { DisplayProps } from '../../displays'; import FGC_BG from './assets/global-bg.png'; import styled from '@emotion/styled'; import { AllianceResult } from './components/alliance-result'; +import MatchTitle from './components/match-title'; const Container = styled.div` background-image: url(${FGC_BG}); @@ -43,17 +44,7 @@ const ResultsText = styled.div` `; const InfoContainer = styled.div` - background-color: #ffffff; - border-radius: 1em; - padding: 0.5em; - text-align: center; - font-weight: bold; - font-size: 2em; - width: 24vw; -`; - -const InfoText = styled.div` - line-height: 1.25em; + width: 40vw; `; export const MatchResults: FC = ({ match, ranks }) => { @@ -63,8 +54,7 @@ export const MatchResults: FC = ({ match, ranks }) => { RESULTS - {match.name} - Field {match.fieldNumber} + diff --git a/front-end/src/components/animations/fade-in-out.tsx b/front-end/src/components/animations/fade-in-out.tsx new file mode 100644 index 00000000..dd791f14 --- /dev/null +++ b/front-end/src/components/animations/fade-in-out.tsx @@ -0,0 +1,52 @@ +import { Box } from '@mui/material'; +import React, { useEffect, useState } from 'react'; + +interface AnimationProps { + in: boolean; + children: React.ReactNode; + duration?: number; + inDelay?: number; + outDelay?: number; +} + +const FadeInOut: React.FC = ({ + in: elementIn, + children, + duration, + inDelay, + outDelay +}) => { + const [localIn, setLocalIn] = useState(false); + + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + const delay = elementIn ? inDelay : outDelay; + + if (typeof delay === 'number') { + timeoutId = setTimeout(() => { + setLocalIn(elementIn); + }, delay * 1000); + } else { + setLocalIn(elementIn); + } + + return () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + }, [elementIn, inDelay, outDelay]); + return ( + + {children} + + ); +}; + +export default FadeInOut; diff --git a/front-end/src/components/animations/index.ts b/front-end/src/components/animations/index.ts new file mode 100644 index 00000000..51109bb7 --- /dev/null +++ b/front-end/src/components/animations/index.ts @@ -0,0 +1,7 @@ +import FadeInOut from "./fade-in-out"; +import SlideInBottom from "./slide-in-bottom"; + +export { + FadeInOut, + SlideInBottom +} \ No newline at end of file diff --git a/front-end/src/components/animations/slide-in-bottom.tsx b/front-end/src/components/animations/slide-in-bottom.tsx new file mode 100644 index 00000000..6eef5c8d --- /dev/null +++ b/front-end/src/components/animations/slide-in-bottom.tsx @@ -0,0 +1,56 @@ +import { Box } from '@mui/material'; +import React, { useEffect, useState } from 'react'; + +interface AnimationProps { + in: boolean; + children: React.ReactNode; + duration?: number; + inDelay?: number; + outDelay?: number; +} + +const SlideInBottom: React.FC = ({ + in: elementIn, + children, + duration, + inDelay, + outDelay +}) => { + const [localIn, setLocalIn] = useState(false); + + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + const delay = elementIn ? inDelay : outDelay; + + if (typeof delay === 'number') { + timeoutId = setTimeout(() => { + setLocalIn(elementIn); + }, delay * 1000); + } else { + setLocalIn(elementIn); + } + + return () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + }, [elementIn, inDelay, outDelay]); + + return ( + + {children} + + ); +}; + +export default SlideInBottom; diff --git a/front-end/src/components/util/absoloute-locator.tsx b/front-end/src/components/util/absoloute-locator.tsx new file mode 100644 index 00000000..6235263d --- /dev/null +++ b/front-end/src/components/util/absoloute-locator.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +interface AbsoluteLodatorProps { + children: React.ReactNode; + top?: number; + left?: number; + right?: number; + bottom?: number; +} + +const AbsoluteLodator: React.FC = ({ + children, + top, + left, + bottom, + right +}) => { + return ( +
+ {children} +
+ ); +}; + +export default AbsoluteLodator; diff --git a/front-end/src/layouts/chroma-layout.tsx b/front-end/src/layouts/chroma-layout.tsx index e8b30bcf..85e5f163 100644 --- a/front-end/src/layouts/chroma-layout.tsx +++ b/front-end/src/layouts/chroma-layout.tsx @@ -8,7 +8,24 @@ interface Props { export const ChromaLayout: FC = ({ children }) => { return ( - {children} + {/* because mui is dumb */} + + + {children} + ); };