From 6a04ef0f62dad6db972211e4ede37508a6a8bb8a Mon Sep 17 00:00:00 2001 From: Daniel Scalzi Date: Sun, 13 Sep 2020 22:18:08 -0400 Subject: [PATCH] Move landing media buttons to their own component. The media buttons can either open a link or perform an action. TODO: Convert CSS from px to rem. --- src/renderer/components/Application.tsx | 11 +- src/renderer/components/landing/Landing.css | 124 ------------- src/renderer/components/landing/Landing.tsx | 121 ++++++------- .../landing/mediabutton/MediaButton.css | 131 ++++++++++++++ .../landing/mediabutton/MediaButton.tsx | 169 ++++++++++++++++++ 5 files changed, 362 insertions(+), 194 deletions(-) create mode 100644 src/renderer/components/landing/mediabutton/MediaButton.css create mode 100644 src/renderer/components/landing/mediabutton/MediaButton.tsx diff --git a/src/renderer/components/Application.tsx b/src/renderer/components/Application.tsx index d935e9d..95dd4c0 100644 --- a/src/renderer/components/Application.tsx +++ b/src/renderer/components/Application.tsx @@ -288,6 +288,7 @@ class Application extends React.Component { // TODO determine correct view @@ -310,11 +311,11 @@ class Application extends React.Component { } } + private readonly mediaButtons = [ + { + href: 'https://github.com/dscalzi/HeliosLauncher', + type: MediaButtonType.LINK, + disabled: false + }, + { + href: '#', + type: MediaButtonType.TWITTER, + disabled: true + }, + { + href: '#', + type: MediaButtonType.INSTAGRAM, + disabled: true + }, + { + href: '#', + type: MediaButtonType.YOUTUBE, + disabled: true + }, + { + href: 'https://discord.gg/zNWUXdt', + type: MediaButtonType.DISCORD, + disabled: false, + tooltip: 'Discord' + } + ] + + private getExternalMediaButtons = (): JSX.Element[] => { + const ret: JSX.Element[] = [] + for(const { href, type, disabled, tooltip } of this.mediaButtons) { + ret.push( + + ) + } + return ret + } + + private onSettingsClick = async (): Promise => { + console.log('Settings clicked') + } + /* Render */ render(): JSX.Element { @@ -162,74 +212,15 @@ class Landing extends React.Component {
-
- -
+
- - - - - + {this.getExternalMediaButtons()}
diff --git a/src/renderer/components/landing/mediabutton/MediaButton.css b/src/renderer/components/landing/mediabutton/MediaButton.css new file mode 100644 index 0000000..3952bf0 --- /dev/null +++ b/src/renderer/components/landing/mediabutton/MediaButton.css @@ -0,0 +1,131 @@ +/* Container object which wraps an icon to ensure fluid transitions. */ +.mediaContainer { + display: flex; + justify-content: center; + align-items: center; + height: 27px; + position: relative; +} + +/* Social media icon shared styles. */ +.mediaSVG { + fill: #ffffff; + height: 12px; + transition: 0.25s ease; + cursor: pointer; + height: 12px; + width: 25px; +} +.mediaSVG:hover, +.mediaURL:focus .mediaSVG, +.mediaSVG:active { + height: 20px; +} + +/* Social media URL shared styles. */ +.mediaURL { + outline: none; + display: flex; + align-items: center; +} + +/* Internal media button shared styles. */ +.mediaButton { + background: none; + border: none; + padding: 0; + display: flex; + align-items: center; + outline: none; +} + +/* Twitter icon colors. */ +.twitterSVG:hover, +.twitterURL:focus .twitterSVG { + fill: #1da1f2; +} +.twitterSVG:active { + fill: #1b8dd4; +} + +/* Instagram icon colors. */ +.instagramSVG:hover, +.instagramURL:focus .instagramSVG { + fill: url('#instaFill') + /*fill: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%); */ +} +.instagramSVG:active { + fill: url('#instaFill') +} + +/* Youtube icon colors. */ +.youtubeSVG:hover, +.youtubeURL:focus .youtubeSVG { + fill: #f00; +} +.youtubeSVG:active { + fill: #ea0202; +} + +/* Discord icon colors. */ +.discordSVG:hover, +.discordURL:focus .discordSVG { + fill: #7288d9; +} +.discordSVG:active { + fill: #657ac4; +} + +/* Settings icon colors. */ +.settingsSVG { + stroke: #ffffff; + height: 15px; +} +.mediaButton:hover .settingsSVG, +.mediaButton:focus .settingsSVG, +.mediaButton:active .settingsSVG { + height: 23px; +} + +.mediaTooltip { + visibility: hidden; + opacity: 0; + width: 75px; + height: 20px; + background-color: rgba(0, 0, 0, 0.75); + text-align: center; + border-radius: 4px; + position: absolute; + z-index: 1; + right: 130%; + font-size: 12px; + line-height: 20px; + transition: visibility 0s linear 0.25s, opacity 0.25s ease; + color: #fff; +} +.mediaTooltip::after { + content: " "; + position: absolute; + top: 50%; + left: 100%; + margin-top: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent transparent transparent rgba(0, 0, 0, 0.75); +} + +.mediaURL:hover .mediaTooltip, +.mediaURL:focus .mediaTooltip, +.mediaURL:active .mediaTooltip { + visibility: visible; + opacity: 1; + transition-delay:0s; +} + +.mediaButton:hover .mediaTooltip, +.mediaButton:focus .mediaTooltip, +.mediaButton:active .mediaTooltip { + visibility: visible; + opacity: 1; + transition-delay:0s; +} \ No newline at end of file diff --git a/src/renderer/components/landing/mediabutton/MediaButton.tsx b/src/renderer/components/landing/mediabutton/MediaButton.tsx new file mode 100644 index 0000000..159506d --- /dev/null +++ b/src/renderer/components/landing/mediabutton/MediaButton.tsx @@ -0,0 +1,169 @@ +import { LoggerUtil } from 'common/logging/loggerutil' +import * as React from 'react' + +import './MediaButton.css' + +// Pass href to render with an anchor. +// Pass action to render with a button. + +export interface MediaButtonProps { + type: MediaButtonType + href?: string + action?: () => Promise + disabled?: boolean + tooltip?: string +} + +export enum MediaButtonType { + LINK = 'LINK', + TWITTER = 'TWITTER', + INSTAGRAM = 'INSTAGRAM', + YOUTUBE = 'YOUTUBE', + DISCORD = 'DISCORD', + SETTINGS = 'SETTINGS' +} + +export class MediaButton extends React.Component { + + private readonly logger = LoggerUtil.getLogger('MediaButton') + + private getSVGClassName = (type: MediaButtonType): string => { + return `${type.toLowerCase()}SVG` + } + + private getAnchorClassName = (type: MediaButtonType): string => { + return `${type.toLowerCase()}URL` + } + + private getButtonClassName = (type: MediaButtonType): string => { + return `${type.toLowerCase()}MediaButton` + } + + private readonly mediaContentMap: {[key in MediaButtonType]: JSX.Element} = { + [MediaButtonType.LINK]: ( + + + + + + + ), + [MediaButtonType.TWITTER]: ( + + + + + + ), + [MediaButtonType.INSTAGRAM]: ( + + + + + + + + + + + + + + + + + ), + [MediaButtonType.YOUTUBE]: ( + + + + + + ), + [MediaButtonType.DISCORD]: ( + + + + + + ), + [MediaButtonType.SETTINGS]: ( + + + + ) + } + + private blurOnClick = (): void => { + if(document.activeElement instanceof HTMLElement) { + document.activeElement.blur() + } + } + + private handleButtonClick = (action: () => Promise): () => Promise => { + return async () => { + this.blurOnClick() + try { + await action() + } catch(err) { + this.logger.error('Uncaught error in media button action.', err) + } + } + } + + private renderTooltip = (): JSX.Element => { + if(this.props.tooltip) { + return
{this.props.tooltip}
+ } else { + return <> + } + } + + private renderContent = (): JSX.Element => { + + const internalContent: JSX.Element = ( + <> + {this.mediaContentMap[this.props.type]} + {this.renderTooltip()} + + ) + + if(this.props.href) { + // Render anchor + return ( + + {internalContent} + + ) + } else if(this.props.action) { + // Render button + return ( + + ) + } else { + return <> + No Content + + } + } + + render(): JSX.Element { + return <> +
+ {this.renderContent()} +
+ + } + +} \ No newline at end of file