Move theme selector to the header and improved about page and styling
This commit is contained in:
11
src/components/Code.js
Normal file
11
src/components/Code.js
Normal 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;
|
||||
@@ -23,43 +23,12 @@ const HorizontalStack = styled.div`
|
||||
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 = ({
|
||||
paused,
|
||||
started,
|
||||
pauseSnake,
|
||||
stopSnake,
|
||||
startSnake,
|
||||
theme,
|
||||
changeTheme,
|
||||
fps,
|
||||
zoom,
|
||||
setFps,
|
||||
@@ -67,14 +36,6 @@ export const ControlPanel = ({
|
||||
zoomOut
|
||||
}) => (
|
||||
<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" }}>
|
||||
<span>
|
||||
Zoom: <b>{zoom}</b>
|
||||
|
||||
10
src/components/DocumentSection.js
Normal file
10
src/components/DocumentSection.js
Normal 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;
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import ThemeSelector from "./ThemeSelector.js";
|
||||
|
||||
const MenuPanel = styled.ul`
|
||||
display: flex;
|
||||
@@ -8,17 +9,18 @@ const MenuPanel = styled.ul`
|
||||
|
||||
const MenuItem = styled.li`
|
||||
display: inline-block;
|
||||
margin-right: 0.5rem;
|
||||
min-width: 3.125rem;
|
||||
`;
|
||||
|
||||
const MenuButton = styled(NavLink)`
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border: 1px solid silver;
|
||||
border: 1px solid ${({ theme }) => theme.button.borderColor};
|
||||
background: ${props => props.theme.header.menuButton.background};
|
||||
color: ${props => props.theme.header.menuButton.color};
|
||||
padding: 0.3125rem;
|
||||
margin-right: 0.5rem;
|
||||
border-radius: 0.125rem;
|
||||
min-width: 3.125rem;
|
||||
&.active {
|
||||
@@ -44,6 +46,9 @@ const HeaderMenu = () => (
|
||||
About
|
||||
</MenuButton>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<ThemeSelector border />
|
||||
</MenuItem>
|
||||
</MenuPanel>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Page = styled.div`
|
||||
max-width: 60rem;
|
||||
margin: 0 auto;
|
||||
padding: 0.5em;
|
||||
`;
|
||||
const Page = styled.div``;
|
||||
|
||||
export default Page;
|
||||
|
||||
30
src/components/PageSection.js
Normal file
30
src/components/PageSection.js
Normal 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;
|
||||
58
src/components/ThemeSelector.js
Normal file
58
src/components/ThemeSelector.js
Normal 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);
|
||||
@@ -1,7 +1,31 @@
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
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;
|
||||
|
||||
@@ -1,59 +1,121 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import Title from "../components/Title.js";
|
||||
import PageSection from "../components/PageSection.js";
|
||||
import Code from "../components/Code.js";
|
||||
|
||||
const Code = styled.code`
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
const MainPageSection = styled(PageSection)`
|
||||
background-color: ${({ theme }) => theme.colors.pageSectionMainColor};
|
||||
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 = () => (
|
||||
<React.Fragment>
|
||||
<Title large>About Crafity Snake</Title>
|
||||
<p>Play snake in your browser using HTML 5 and Javascript.</p>
|
||||
<Title>Introduction</Title>
|
||||
<p>
|
||||
This experiment has been created to test out React, Redux and Styled Components and see if it is possible to
|
||||
create a simple web based game like snake. The source code for this game is open source and free to download and
|
||||
change.
|
||||
</p>
|
||||
<p>
|
||||
A couple of noticable features in this game are resumable game play using local storage. The page can be reloaded
|
||||
or reopened any time and the game should continue from where it was left.
|
||||
</p>
|
||||
<Title>How to play</Title>
|
||||
<p>
|
||||
You control the snake using the arrow keys or the <Code>hjkl</Code> keys. Everytime the snake eats an apple the
|
||||
tail of the snake grows longer. When the snake touches his own body the game is over.
|
||||
</p>
|
||||
<p>
|
||||
Other keys to control the game are <Code>r</Code> to (re)start the game. The <Code>s</Code> key to stop the game
|
||||
and <Code>p</Code> to pause the game.
|
||||
</p>
|
||||
<Title>Technology</Title>
|
||||
<p>Snake has been developed with the folowing technologies.</p>
|
||||
<ul>
|
||||
<li>React + React Router</li>
|
||||
<li>Redux</li>
|
||||
<li>Styled Components</li>
|
||||
<li>Webpack + Babel</li>
|
||||
<li>Jest</li>
|
||||
<li>ESLint</li>
|
||||
<li>pre-commit</li>
|
||||
<li>Docker</li>
|
||||
</ul>
|
||||
<Title>Source Code</Title>
|
||||
<p>The source code is hosted on Crafity'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>
|
||||
<MainPageSection content={PageSection.content.center}>
|
||||
<Layout style={{ justifyContent: "center" }} direction={"horizontal"}>
|
||||
<Layout style={{ width: "30rem", placeContent: "center" }} direction={"vertical"}>
|
||||
<Title larger>Crafity Snake</Title>
|
||||
<Title sub>Play snake with HTML 5 and Javascript.</Title>
|
||||
</Layout>
|
||||
<Icon>🐍</Icon>
|
||||
</Layout>
|
||||
</MainPageSection>
|
||||
<StandardPageSection content={PageSection.content.center}>
|
||||
<Title>Introduction</Title>
|
||||
<p>
|
||||
This experiment has been created to test out React, Redux and Styled Components and see if it is possible to
|
||||
create a simple web based game like snake. The source code for this game is open source and free to download and
|
||||
change.
|
||||
</p>
|
||||
<p>
|
||||
A couple of noticable features in this game are resumable game play using local storage. The page can be
|
||||
reloaded or reopened any time and the game should continue from where it was left.
|
||||
</p>
|
||||
</StandardPageSection>
|
||||
<AlternatePageSection content={PageSection.content.center}>
|
||||
<Title>How to play</Title>
|
||||
<p>
|
||||
You control the snake using the arrow keys or the <Code>hjkl</Code> keys. Everytime the snake eats an apple the
|
||||
tail of the snake grows longer. When the snake touches his own body the game is over.
|
||||
</p>
|
||||
<p>
|
||||
Other keys to control the game are <Code>r</Code> to (re)start the game. The <Code>s</Code> key to stop the game
|
||||
and <Code>p</Code> to pause the game.
|
||||
</p>
|
||||
</AlternatePageSection>
|
||||
<StandardPageSection content={PageSection.content.center}>
|
||||
<Title>Technology</Title>
|
||||
<p>Snake has been developed with the folowing technologies.</p>
|
||||
<ul>
|
||||
<li>React + React Router</li>
|
||||
<li>Redux</li>
|
||||
<li>Styled Components</li>
|
||||
<li>Webpack + Babel</li>
|
||||
<li>Jest</li>
|
||||
<li>ESLint</li>
|
||||
<li>pre-commit</li>
|
||||
<li>Docker</li>
|
||||
</ul>
|
||||
</StandardPageSection>
|
||||
<AlternatePageSection content={PageSection.content.center}>
|
||||
<Title>Source Code</Title>
|
||||
<p>The source code is hosted on Crafity'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>
|
||||
);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const GlobalStyle = createGlobalStyle`
|
||||
background: ${props => props.theme.body.background};
|
||||
color: ${props => props.theme.body.color};
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
body, input, select {
|
||||
font-family: ${({ theme }) => theme.body.fontFamily};
|
||||
|
||||
@@ -11,7 +11,7 @@ export const colors = {
|
||||
colorInactive: "#97727e",
|
||||
colorAlternate: "#ecb1c5",
|
||||
shadowColor: "#222",
|
||||
borderColor: "#222",
|
||||
borderColor: "silver",
|
||||
borderColorActive: "#e6b4c4",
|
||||
borderColorInactive: "#97727e",
|
||||
spinnerShadow: "#444",
|
||||
|
||||
@@ -21,7 +21,10 @@ export const defaultColors = {
|
||||
snakePartHueAlive: "340",
|
||||
snakePartHueDied: "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);
|
||||
@@ -45,6 +48,7 @@ export const renderTheme = themeColors => {
|
||||
borderWidth: `${1 / 16}rem`
|
||||
},
|
||||
select: {
|
||||
borderColor: colors.borderColor,
|
||||
color: colors.selectColor
|
||||
},
|
||||
header: {
|
||||
|
||||
@@ -18,7 +18,10 @@ export const colors = {
|
||||
spinnerHighlight: "#db7093",
|
||||
cardFoldHighlight: "#ad5a75",
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user