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;
|
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>
|
||||||
|
|||||||
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 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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
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`
|
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;
|
||||||
|
|||||||
@@ -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'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'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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user