Move theme selector to the header and improved about page and styling

This commit is contained in:
2019-09-09 11:49:39 +02:00
parent ee610cc9b6
commit 9fc48df1ce
13 changed files with 265 additions and 100 deletions

11
src/components/Code.js Normal file
View File

@@ -0,0 +1,11 @@
import styled from "styled-components";
export const Code = styled.code`
display: inline-block;
color: white;
background-color: gray;
padding: 0 0.2rem;
line-height: 1.4rem;
`;
export default Code;

View File

@@ -23,43 +23,12 @@ const HorizontalStack = styled.div`
place-content: space-between; place-content: space-between;
`; `;
const ThemeSelector = styled.select`
appearance: none;
border: 1px solid transparent;
background: none;
border-radius: 3px;
// padding: 5px;
line-height: 1.5em;
color: ${({ theme }) => theme.body.color};
font-size: ${({ theme }) => theme.body.fontSize};
font-family: ${({ theme }) => theme.body.fontFamily};
height: 100%;
width: 100%;
cursor: pointer;
background-image: url('data:image/svg+xml;utf8,
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<polygon points="0,3 16,3 8,13" fill="${({ theme }) => theme.select.color.replace("#", "%23")}" />
</svg>');
background-repeat: no-repeat;
background-position: right 0 center;
background-size: 1rem 1rem;
outline-offset: 3px;
&:active,
&:focus,
&:hover {
outline: 1px solid silver;
}
`;
export const ControlPanel = ({ export const ControlPanel = ({
paused, paused,
started, started,
pauseSnake, pauseSnake,
stopSnake, stopSnake,
startSnake, startSnake,
theme,
changeTheme,
fps, fps,
zoom, zoom,
setFps, setFps,
@@ -67,14 +36,6 @@ export const ControlPanel = ({
zoomOut zoomOut
}) => ( }) => (
<Layout> <Layout>
<HorizontalStack style={{ minHeight: "2rem", lineHeight: "2rem" }}>
<ThemeSelector onChange={e => changeTheme(e.target.value)} value={theme}>
<option value="light">Light</option>
<option value="dark">Miami Night</option>
<option value="darkOcean">Dark Ocean</option>
<option value="default">Black / White</option>
</ThemeSelector>
</HorizontalStack>
<HorizontalStack style={{ minHeight: "2rem", lineHeight: "2rem" }}> <HorizontalStack style={{ minHeight: "2rem", lineHeight: "2rem" }}>
<span> <span>
Zoom: <b>{zoom}</b> Zoom: <b>{zoom}</b>

View File

@@ -0,0 +1,10 @@
import React from "react";
import styled from "styled-components";
const Section = styled.div``;
export const PageSection = ({ children }) => {
return <Section>{children}</Section>;
};
export default PageSection;

View File

@@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import ThemeSelector from "./ThemeSelector.js";
const MenuPanel = styled.ul` const MenuPanel = styled.ul`
display: flex; display: flex;
@@ -8,17 +9,18 @@ const MenuPanel = styled.ul`
const MenuItem = styled.li` const MenuItem = styled.li`
display: inline-block; display: inline-block;
margin-right: 0.5rem;
min-width: 3.125rem;
`; `;
const MenuButton = styled(NavLink)` const MenuButton = styled(NavLink)`
display: inline-block; display: inline-block;
text-align: center; text-align: center;
text-decoration: none; text-decoration: none;
border: 1px solid silver; border: 1px solid ${({ theme }) => theme.button.borderColor};
background: ${props => props.theme.header.menuButton.background}; background: ${props => props.theme.header.menuButton.background};
color: ${props => props.theme.header.menuButton.color}; color: ${props => props.theme.header.menuButton.color};
padding: 0.3125rem; padding: 0.3125rem;
margin-right: 0.5rem;
border-radius: 0.125rem; border-radius: 0.125rem;
min-width: 3.125rem; min-width: 3.125rem;
&.active { &.active {
@@ -44,6 +46,9 @@ const HeaderMenu = () => (
About About
</MenuButton> </MenuButton>
</MenuItem> </MenuItem>
<MenuItem>
<ThemeSelector border />
</MenuItem>
</MenuPanel> </MenuPanel>
); );

View File

@@ -1,9 +1,5 @@
import styled from "styled-components"; import styled from "styled-components";
const Page = styled.div` const Page = styled.div``;
max-width: 60rem;
margin: 0 auto;
padding: 0.5em;
`;
export default Page; export default Page;

View File

@@ -0,0 +1,30 @@
import React from "react";
import styled, { css } from "styled-components";
export const Content = styled.div`
${({ content }) =>
content === PageSection.content.center &&
css`
max-width: 60rem;
margin: 0 auto;
// padding: 0.5em;
`}
`;
const contentPosition = {
center: "center",
stretch: "stretch"
};
export const PageSection = ({ content, children, className } = { content: contentPosition.stretch }) => {
return (
<div className={className}>
<Content content={content}>{children}</Content>
</div>
);
};
PageSection.content = contentPosition;
PageSection.Content = Content;
export default PageSection;

View File

@@ -0,0 +1,58 @@
import React from "react";
import { connect } from "react-redux";
import styled from "styled-components";
import { module as uiModule, changeTheme } from "../redux/ui.js";
const StyledSelect = styled.select`
appearance: none;
border: 1px solid ${({ border, theme }) => (border ? theme.select.borderColor : "transparent")};
background: none;
border-radius: 3px;
padding: 0 1.8rem 0 .5rem;
line-height: 1.5em;
color: ${({ theme }) => theme.body.color};
font-size: ${({ theme }) => theme.body.fontSize};
font-family: ${({ theme }) => theme.body.fontFamily};
height: 100%;
min-width: 8.375rem;
// width: 100%;
cursor: pointer;
background-image: url('data:image/svg+xml;utf8,
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<polygon points="0,3 16,3 8,13" fill="${({ theme }) => theme.select.color.replace("#", "%23")}" />
</svg>');
background-repeat: no-repeat;
background-position: right ${({ border }) => (border ? ".5rem" : 0)} center;
background-size: 1rem 1rem;
outline-offset: ${({ border }) => (border ? 0 : ".1875rem")};
&:active,
&:focus,
&:hover {
outline: 1px solid silver;
}
`;
const ThemeSelector = ({ theme, changeTheme, border }) => {
return (
<StyledSelect border={border} onChange={e => changeTheme(e.target.value)} value={theme}>
<option value="light">Light</option>
<option value="dark">Miami Night</option>
<option value="darkOcean">Dark Ocean</option>
<option value="default">Black / White</option>
</StyledSelect>
);
};
const mapStateToProps = state => ({
...state[uiModule.name]
});
const mapActionsToProps = {
changeTheme
};
export default connect(
mapStateToProps,
mapActionsToProps
)(ThemeSelector);

View File

@@ -1,7 +1,31 @@
import styled from "styled-components"; import styled, { css } from "styled-components";
const Title = styled.h1` const Title = styled.h1`
font-size: ${props => (props.large ? "3rem" : "1.5rem")}; margin: 0;
font-size: 1.5rem;
line-height: 4rem;
${props =>
props.large &&
css`
font-size: 3rem;
line-height: 4rem;
`}
${props =>
props.larger &&
css`
font-size: 5rem;
line-height: 6rem;
`}
${props =>
props.sub &&
css`
text-transform: uppercase;
font-size: 1.4rem;
line-height: 1.3rem;
`};
`; `;
export default Title; export default Title;

View File

@@ -1,59 +1,121 @@
import React from "react"; import React from "react";
import styled from "styled-components"; import styled, { css } from "styled-components";
import Title from "../components/Title.js"; import Title from "../components/Title.js";
import PageSection from "../components/PageSection.js";
import Code from "../components/Code.js";
const Code = styled.code` const MainPageSection = styled(PageSection)`
font-weight: bold; background-color: ${({ theme }) => theme.colors.pageSectionMainColor};
color: white; min-height: 20rem;
${PageSection.Content} {
box-sizing: border-box;
padding: 1rem 2rem;
}
`;
const StandardPageSection = styled(PageSection)`
background-color: ${({ theme }) => theme.colors.pageSectionStandardColor};
${PageSection.Content} {
box-sizing: border-box;
padding: 2rem 8rem;
}
`;
const AlternatePageSection = styled(PageSection)`
background-color: ${({ theme }) => theme.colors.pageSectionAlternateColor};
${PageSection.Content} {
box-sizing: border-box;
padding: 2rem 8rem;
}
`;
const Icon = styled.div`
font-size: 150pt;
display: inline-block;
`;
const Layout = styled.div`
display: flex;
width: 100%;
// place-content: center;
// justify-content: space-around;
// align-content: space-around;
margin-top: 2rem;
${({ direction }) =>
direction === "horizontal" &&
css`
flex-direction: row;
`}
${({ direction }) =>
direction === "vertical" &&
css`
flex-direction: column;
`}
`; `;
export const About = () => ( export const About = () => (
<React.Fragment> <React.Fragment>
<Title large>About Crafity Snake</Title> <MainPageSection content={PageSection.content.center}>
<p>Play snake in your browser using HTML 5 and Javascript.</p> <Layout style={{ justifyContent: "center" }} direction={"horizontal"}>
<Title>Introduction</Title> <Layout style={{ width: "30rem", placeContent: "center" }} direction={"vertical"}>
<p> <Title larger>Crafity Snake</Title>
This experiment has been created to test out React, Redux and Styled Components and see if it is possible to <Title sub>Play snake with HTML 5 and Javascript.</Title>
create a simple web based game like snake. The source code for this game is open source and free to download and </Layout>
change. <Icon>🐍</Icon>
</p> </Layout>
<p> </MainPageSection>
A couple of noticable features in this game are resumable game play using local storage. The page can be reloaded <StandardPageSection content={PageSection.content.center}>
or reopened any time and the game should continue from where it was left. <Title>Introduction</Title>
</p> <p>
<Title>How to play</Title> This experiment has been created to test out React, Redux and Styled Components and see if it is possible to
<p> create a simple web based game like snake. The source code for this game is open source and free to download and
You control the snake using the arrow keys or the <Code>hjkl</Code> keys. Everytime the snake eats an apple the change.
tail of the snake grows longer. When the snake touches his own body the game is over. </p>
</p> <p>
<p> A couple of noticable features in this game are resumable game play using local storage. The page can be
Other keys to control the game are <Code>r</Code> to (re)start the game. The <Code>s</Code> key to stop the game reloaded or reopened any time and the game should continue from where it was left.
and <Code>p</Code> to pause the game. </p>
</p> </StandardPageSection>
<Title>Technology</Title> <AlternatePageSection content={PageSection.content.center}>
<p>Snake has been developed with the folowing technologies.</p> <Title>How to play</Title>
<ul> <p>
<li>React + React Router</li> You control the snake using the arrow keys or the <Code>hjkl</Code> keys. Everytime the snake eats an apple the
<li>Redux</li> tail of the snake grows longer. When the snake touches his own body the game is over.
<li>Styled Components</li> </p>
<li>Webpack + Babel</li> <p>
<li>Jest</li> Other keys to control the game are <Code>r</Code> to (re)start the game. The <Code>s</Code> key to stop the game
<li>ESLint</li> and <Code>p</Code> to pause the game.
<li>pre-commit</li> </p>
<li>Docker</li> </AlternatePageSection>
</ul> <StandardPageSection content={PageSection.content.center}>
<Title>Source Code</Title> <Title>Technology</Title>
<p>The source code is hosted on Crafity&apos;s git repositories at the following location:</p> <p>Snake has been developed with the folowing technologies.</p>
<p> <ul>
<a href="https://git.crafity.com">Crafity Snake</a> <li>React + React Router</li>
</p> <li>Redux</li>
<p>After donwloading the source code run the following commands:</p> <li>Styled Components</li>
<p> <li>Webpack + Babel</li>
<Code>npm install</Code> <li>Jest</li>
</p> <li>ESLint</li>
<p> <li>pre-commit</li>
<Code>npm run dev</Code> <li>Docker</li>
</p> </ul>
</StandardPageSection>
<AlternatePageSection content={PageSection.content.center}>
<Title>Source Code</Title>
<p>The source code is hosted on Crafity&apos;s git repositories at the following location:</p>
<p>
<a href="https://git.crafity.com">Crafity Snake</a>
</p>
<p>After donwloading the source code run the following commands:</p>
<p>
<Code>npm install</Code>
</p>
<p>
<Code>npm run dev</Code>
</p>
</AlternatePageSection>
</React.Fragment> </React.Fragment>
); );

View File

@@ -9,6 +9,7 @@ const GlobalStyle = createGlobalStyle`
background: ${props => props.theme.body.background}; background: ${props => props.theme.body.background};
color: ${props => props.theme.body.color}; color: ${props => props.theme.body.color};
min-height: 100vh; min-height: 100vh;
-webkit-font-smoothing: antialiased;
} }
body, input, select { body, input, select {
font-family: ${({ theme }) => theme.body.fontFamily}; font-family: ${({ theme }) => theme.body.fontFamily};

View File

@@ -11,7 +11,7 @@ export const colors = {
colorInactive: "#97727e", colorInactive: "#97727e",
colorAlternate: "#ecb1c5", colorAlternate: "#ecb1c5",
shadowColor: "#222", shadowColor: "#222",
borderColor: "#222", borderColor: "silver",
borderColorActive: "#e6b4c4", borderColorActive: "#e6b4c4",
borderColorInactive: "#97727e", borderColorInactive: "#97727e",
spinnerShadow: "#444", spinnerShadow: "#444",

View File

@@ -21,7 +21,10 @@ export const defaultColors = {
snakePartHueAlive: "340", snakePartHueAlive: "340",
snakePartHueDied: "0", snakePartHueDied: "0",
snakePartLightness: "0%", snakePartLightness: "0%",
selectColor: "black" selectColor: "black",
pageSectionMainColor: "#eaeaea",
pageSectionStandardColor: "white",
pageSectionAlternateColor: "#fafafa"
}; };
const mapRange = (l, i, min, max) => Math.round((max - min) * ((l - i) / l) + min); const mapRange = (l, i, min, max) => Math.round((max - min) * ((l - i) / l) + min);
@@ -45,6 +48,7 @@ export const renderTheme = themeColors => {
borderWidth: `${1 / 16}rem` borderWidth: `${1 / 16}rem`
}, },
select: { select: {
borderColor: colors.borderColor,
color: colors.selectColor color: colors.selectColor
}, },
header: { header: {

View File

@@ -18,7 +18,10 @@ export const colors = {
spinnerHighlight: "#db7093", spinnerHighlight: "#db7093",
cardFoldHighlight: "#ad5a75", cardFoldHighlight: "#ad5a75",
cardFoldShadow: "#bdb19a", cardFoldShadow: "#bdb19a",
selectColor: "#db7093" selectColor: "#db7093",
pageSectionMainColor: "papayawhip",
pageSectionStandardColor: "white",
pageSectionAlternateColor: "#fafafa"
}; };
const mapRange = (l, i, min, max) => Math.round((max - min) * ((l - i) / l) + min); const mapRange = (l, i, min, max) => Math.round((max - min) * ((l - i) / l) + min);