Implement tooltip and clean up/refactor code base
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
import styled from "styled-components";
|
import styled, { keyframes } from "styled-components";
|
||||||
|
|
||||||
|
const fade = keyframes`
|
||||||
|
from { transform: scale(.7) }
|
||||||
|
to { transform: scale(1) }
|
||||||
|
`;
|
||||||
|
|
||||||
const Apple = styled.div.attrs(({ theme, zoom }) => ({
|
const Apple = styled.div.attrs(({ theme, zoom }) => ({
|
||||||
style: {
|
style: {
|
||||||
height: theme.snake.cell.size * (zoom || 1) + "rem",
|
height: theme.snake.cell.size * (zoom || 1) + "rem",
|
||||||
width: theme.snake.cell.size * (zoom || 1) + "rem",
|
width: theme.snake.cell.size * (zoom || 1) + "rem",
|
||||||
border: theme.snake.cell.border,
|
|
||||||
fontSize: (zoom || 1) * 1.3 + "rem",
|
fontSize: (zoom || 1) * 1.3 + "rem",
|
||||||
lineHeight: (zoom || 1) * 1.7 + "rem",
|
lineHeight: (zoom || 1) * 1.7 + "rem",
|
||||||
paddingLeft: (zoom || 1) * 0.0625 + "rem"
|
paddingLeft: (zoom || 1) * 0.0625 + "rem"
|
||||||
@@ -14,9 +18,10 @@ const Apple = styled.div.attrs(({ theme, zoom }) => ({
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
// content: "${props => (!props.died ? "🍎" : "🐛")}";
|
content: "${props => (!props.died ? "🍎" : "🐛")}";
|
||||||
content: "🍎";
|
// content: "🍎";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
animation: ${fade} 1s ease-out alternate infinite;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
const SharedStyle = () => `
|
const SharedStyle = () => `
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
@@ -21,6 +21,11 @@ const SharedStyle = () => `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ButtonWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
`;
|
||||||
|
|
||||||
const StyledButton = styled.button`
|
const StyledButton = styled.button`
|
||||||
${SharedStyle}
|
${SharedStyle}
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
@@ -39,8 +44,30 @@ const StyledNavLink = styled(NavLink)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Tooltip = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border: 1px solid gray;
|
||||||
|
background-color: palevioletred;
|
||||||
|
color: papayawhip;
|
||||||
|
padding: 5px;
|
||||||
|
box-shadow: 1px 1px 3px #d0bfa3;
|
||||||
|
${props =>
|
||||||
|
props.hover
|
||||||
|
? css`
|
||||||
|
transition: opacity 400ms 200ms ease-in;
|
||||||
|
`
|
||||||
|
: css`
|
||||||
|
transition: opacity 100ms ease-out;
|
||||||
|
`}
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: ${props => (props.hover ? 1 : 0)};
|
||||||
|
`;
|
||||||
|
|
||||||
export const Button = (props, refs) => {
|
export const Button = (props, refs) => {
|
||||||
|
const [hover, setHover] = React.useState(false);
|
||||||
const updatedProps = { ...props };
|
const updatedProps = { ...props };
|
||||||
|
let tooltip = null;
|
||||||
|
|
||||||
if (props.onClick) {
|
if (props.onClick) {
|
||||||
updatedProps.onClick = e => e.preventDefault() || props.onClick();
|
updatedProps.onClick = e => e.preventDefault() || props.onClick();
|
||||||
@@ -54,13 +81,20 @@ export const Button = (props, refs) => {
|
|||||||
return StyledNavLink.render(updatedProps);
|
return StyledNavLink.render(updatedProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.tooltip) {
|
||||||
|
tooltip = <Tooltip hover={hover}>{props.tooltip}</Tooltip>;
|
||||||
|
}
|
||||||
|
|
||||||
if (props.icon) {
|
if (props.icon) {
|
||||||
return (
|
return (
|
||||||
<i
|
<ButtonWrapper onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
|
||||||
onClick={updatedProps.onClick}
|
<i
|
||||||
className={`fas fa-${props.icon} ${props.className}`}
|
onClick={updatedProps.onClick}
|
||||||
style={{ display: "inline-block", fontSize: `${props.size || 1}rem` }}
|
className={`fas fa-${props.icon} ${props.className}`}
|
||||||
/>
|
style={{ display: "inline-block", fontSize: `${props.size || 1}rem` }}
|
||||||
|
/>
|
||||||
|
{tooltip}
|
||||||
|
</ButtonWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,10 +66,34 @@ export const ControlPanel = ({
|
|||||||
</div>
|
</div>
|
||||||
</HorizontalStack>
|
</HorizontalStack>
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
<ToggleIconButton toggle={!started} icon="stop-circle" size={buttonSize} onClick={stopSnake} />
|
<ToggleIconButton
|
||||||
<ToggleIconButton toggle={started && !paused} icon="play-circle" size={buttonSize} onClick={startSnake} />
|
toggle={!started}
|
||||||
<ToggleIconButton toggle={paused || !started} icon="pause-circle" size={buttonSize} onClick={pauseSnake} />
|
icon="stop-circle"
|
||||||
<ToggleIconButton toggle={!started} icon="dot-circle" size={buttonSize} onClick={startSnake} />
|
tooltip={"Press 's' to stop"}
|
||||||
|
size={buttonSize}
|
||||||
|
onClick={stopSnake}
|
||||||
|
/>
|
||||||
|
<ToggleIconButton
|
||||||
|
toggle={started && !paused}
|
||||||
|
icon="play-circle"
|
||||||
|
size={buttonSize}
|
||||||
|
onClick={startSnake}
|
||||||
|
tooltip={"Press 'r' to start"}
|
||||||
|
/>
|
||||||
|
<ToggleIconButton
|
||||||
|
toggle={paused || !started}
|
||||||
|
icon="pause-circle"
|
||||||
|
size={buttonSize}
|
||||||
|
onClick={pauseSnake}
|
||||||
|
tooltip={"Press 'p' to pause"}
|
||||||
|
/>
|
||||||
|
<ToggleIconButton
|
||||||
|
toggle={!started}
|
||||||
|
icon="dot-circle"
|
||||||
|
size={buttonSize}
|
||||||
|
onClick={startSnake}
|
||||||
|
tooltip={"Press 'r' to restart"}
|
||||||
|
/>
|
||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
export const SnakePart = styled.div`
|
export const SnakePart = styled.div.attrs(({ theme, zoom }) => ({
|
||||||
width: ${({ theme, zoom }) => theme.snake.cell.size * (zoom || 1)}rem;
|
style: {
|
||||||
height: ${({ theme, zoom }) => theme.snake.cell.size * (zoom || 1)}rem;
|
height: theme.snake.cell.size * (zoom || 1) + "rem",
|
||||||
|
width: theme.snake.cell.size * (zoom || 1) + "rem",
|
||||||
|
fontSize: (zoom || 1) * 1.3 + "rem",
|
||||||
|
lineHeight: (zoom || 1) * 1.7 + "rem",
|
||||||
|
paddingLeft: (zoom || 1) * 0.0625 + "rem"
|
||||||
|
}
|
||||||
|
}))`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: ${props => (props.zoom || 1) * 1.3}rem;
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
line-height: ${props => (props.zoom || 1) * 1.7}rem;
|
|
||||||
padding-left: ${props => (props.zoom || 1) * 0.0625}rem;
|
|
||||||
|
|
||||||
${props =>
|
${props =>
|
||||||
!props.died
|
!props.died
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
pauseSnake,
|
pauseSnake,
|
||||||
updateFrameSnake,
|
updateFrameSnake,
|
||||||
keyPressedSnake,
|
keyPressedSnake,
|
||||||
keys,
|
|
||||||
zoomIn,
|
zoomIn,
|
||||||
zoomOut
|
zoomOut
|
||||||
} from "../redux/snake.js";
|
} from "../redux/snake.js";
|
||||||
@@ -41,7 +40,6 @@ class Snake extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
this.supportedKeys = Object.values(keys);
|
|
||||||
window.me = this;
|
window.me = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +61,6 @@ class Snake extends React.Component {
|
|||||||
grid,
|
grid,
|
||||||
started,
|
started,
|
||||||
paused,
|
paused,
|
||||||
snake,
|
|
||||||
died,
|
died,
|
||||||
fps,
|
fps,
|
||||||
setFps,
|
setFps,
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export const directions = {
|
||||||
|
UP: "UP",
|
||||||
|
DOWN: "DOWN",
|
||||||
|
LEFT: "LEFT",
|
||||||
|
RIGHT: "RIGHT"
|
||||||
|
};
|
||||||
|
|
||||||
|
export default directions;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
export const keys = {
|
||||||
|
UP: "ArrowUp",
|
||||||
|
RIGHT: "ArrowRight",
|
||||||
|
DOWN: "ArrowDown",
|
||||||
|
LEFT: "ArrowLeft",
|
||||||
|
|
||||||
|
EQUAL: "=",
|
||||||
|
PLUS: "+",
|
||||||
|
MINUS: "-",
|
||||||
|
|
||||||
|
h: "h",
|
||||||
|
j: "j",
|
||||||
|
k: "k",
|
||||||
|
l: "l",
|
||||||
|
|
||||||
|
p: "p",
|
||||||
|
s: "s",
|
||||||
|
r: "r",
|
||||||
|
|
||||||
|
1: "1",
|
||||||
|
2: "2",
|
||||||
|
3: "3",
|
||||||
|
4: "4",
|
||||||
|
5: "5",
|
||||||
|
6: "6",
|
||||||
|
7: "7",
|
||||||
|
8: "8",
|
||||||
|
9: "9",
|
||||||
|
0: "0"
|
||||||
|
};
|
||||||
|
|
||||||
|
export default keys;
|
||||||
+1
-10
@@ -1,4 +1,5 @@
|
|||||||
import Module from "./module.js";
|
import Module from "./module.js";
|
||||||
|
import keys from "../enums/keys.js";
|
||||||
|
|
||||||
export const MODULE_NAME = "GAME";
|
export const MODULE_NAME = "GAME";
|
||||||
|
|
||||||
@@ -9,16 +10,6 @@ export const module = new Module(MODULE_NAME, {
|
|||||||
fps: 8
|
fps: 8
|
||||||
});
|
});
|
||||||
|
|
||||||
export const keys = {
|
|
||||||
UP: "ArrowUp",
|
|
||||||
RIGHT: "ArrowRight",
|
|
||||||
DOWN: "ArrowDown",
|
|
||||||
LEFT: "ArrowLeft",
|
|
||||||
EQUAL: "=",
|
|
||||||
PLUS: "+",
|
|
||||||
MINUS: "-"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const [START_LOOP, startLoop] = module.action("START_LOOP");
|
export const [START_LOOP, startLoop] = module.action("START_LOOP");
|
||||||
export const [STOP_LOOP, stopLoop] = module.action("STOP_LOOP");
|
export const [STOP_LOOP, stopLoop] = module.action("STOP_LOOP");
|
||||||
export const [LOOP, loop] = module.action("LOOP", ({ recur, skip } = {}) => ({ recur, skip }));
|
export const [LOOP, loop] = module.action("LOOP", ({ recur, skip } = {}) => ({ recur, skip }));
|
||||||
|
|||||||
+11
-72
@@ -1,12 +1,7 @@
|
|||||||
import Module from "./module.js";
|
import Module from "./module.js";
|
||||||
import {
|
import keys from "../enums/keys.js";
|
||||||
registerCaller,
|
import directions from "../enums/directions.js";
|
||||||
unregisterCaller,
|
import { registerCaller, unregisterCaller, startLoop, stopLoop, subscribeKeyPressed } from "./game.js";
|
||||||
startLoop,
|
|
||||||
stopLoop,
|
|
||||||
subscribeKeyPressed,
|
|
||||||
unsubscribeKeyPressed
|
|
||||||
} from "./game.js";
|
|
||||||
|
|
||||||
const MODULE_NAME = "SNAKE";
|
const MODULE_NAME = "SNAKE";
|
||||||
const DEFAULT_GRID_SIZE = 16;
|
const DEFAULT_GRID_SIZE = 16;
|
||||||
@@ -28,40 +23,6 @@ const initialState = {
|
|||||||
|
|
||||||
/* === Snake Helper Functions ================================================================== */
|
/* === Snake Helper Functions ================================================================== */
|
||||||
|
|
||||||
export const directions = {
|
|
||||||
UP: "UP",
|
|
||||||
DOWN: "DOWN",
|
|
||||||
LEFT: "LEFT",
|
|
||||||
RIGHT: "RIGHT"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const keys = {
|
|
||||||
UP: "ArrowUp",
|
|
||||||
RIGHT: "ArrowRight",
|
|
||||||
DOWN: "ArrowDown",
|
|
||||||
LEFT: "ArrowLeft",
|
|
||||||
|
|
||||||
h: "h",
|
|
||||||
j: "j",
|
|
||||||
k: "k",
|
|
||||||
l: "l",
|
|
||||||
|
|
||||||
snakePart: "snakePart",
|
|
||||||
s: "s",
|
|
||||||
r: "r",
|
|
||||||
|
|
||||||
1: "1",
|
|
||||||
2: "2",
|
|
||||||
3: "3",
|
|
||||||
4: "4",
|
|
||||||
5: "5",
|
|
||||||
6: "6",
|
|
||||||
7: "7",
|
|
||||||
8: "8",
|
|
||||||
9: "9",
|
|
||||||
0: "0"
|
|
||||||
};
|
|
||||||
|
|
||||||
function randomPosition(size) {
|
function randomPosition(size) {
|
||||||
return [Math.round(Math.random() * (size - 1)), Math.round(Math.random() * (size - 1))];
|
return [Math.round(Math.random() * (size - 1)), Math.round(Math.random() * (size - 1))];
|
||||||
}
|
}
|
||||||
@@ -195,7 +156,7 @@ module.reducer(UPDATE_FRAME_SNAKE, state => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the snake hits itself */
|
/* Check if the snake hits itself */
|
||||||
if (newSnake.slice(0, -1).filter((snakePart, index) => comparePositions(snakePart, nextPosition)).length) {
|
if (newSnake.slice(0, -1).filter(snakePart => comparePositions(snakePart, nextPosition)).length) {
|
||||||
started = false;
|
started = false;
|
||||||
died = true;
|
died = true;
|
||||||
}
|
}
|
||||||
@@ -240,7 +201,7 @@ module.after(UPDATE_FRAME_SNAKE, (dispatch, { died }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.middleware(START_SNAKE, (dispatch, { started }) => {
|
module.middleware(START_SNAKE, dispatch => {
|
||||||
dispatch(resetSnake({ started: true, died: false }));
|
dispatch(resetSnake({ started: true, died: false }));
|
||||||
dispatch(registerCaller(UPDATE_FRAME_SNAKE));
|
dispatch(registerCaller(UPDATE_FRAME_SNAKE));
|
||||||
dispatch(subscribeKeyPressed(KEY_PRESSED_SNAKE));
|
dispatch(subscribeKeyPressed(KEY_PRESSED_SNAKE));
|
||||||
@@ -263,7 +224,6 @@ module.middleware(PAUSE_SNAKE, (dispatch, { paused }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line complexity
|
|
||||||
module.middleware(KEY_PRESSED_SNAKE, (dispatch, _, { key }) => {
|
module.middleware(KEY_PRESSED_SNAKE, (dispatch, _, { key }) => {
|
||||||
if (key === keys.UP || key === keys.k) {
|
if (key === keys.UP || key === keys.k) {
|
||||||
dispatch(changeDirection(directions.UP));
|
dispatch(changeDirection(directions.UP));
|
||||||
@@ -280,37 +240,16 @@ module.middleware(KEY_PRESSED_SNAKE, (dispatch, _, { key }) => {
|
|||||||
if (key === keys.s) {
|
if (key === keys.s) {
|
||||||
dispatch(stopSnake());
|
dispatch(stopSnake());
|
||||||
}
|
}
|
||||||
if (key === keys.snakePart) {
|
if (key === keys.p) {
|
||||||
dispatch(pauseSnake());
|
dispatch(pauseSnake());
|
||||||
}
|
}
|
||||||
if (key === keys.r) {
|
if (key === keys.r) {
|
||||||
dispatch(startSnake());
|
dispatch(startSnake());
|
||||||
}
|
}
|
||||||
if (key === keys[1]) {
|
|
||||||
dispatch(setZoomLevel(0.6));
|
/* Map the number keys to a zoom level */
|
||||||
}
|
const keyAsInt = parseInt(key, 10);
|
||||||
if (key === keys[2]) {
|
if (!isNaN(keyAsInt) && keyAsInt > 0 && keyAsInt <= 9) {
|
||||||
dispatch(setZoomLevel(0.8));
|
dispatch(setZoomLevel(0.6 + (keyAsInt - 1) * 0.2));
|
||||||
}
|
|
||||||
if (key === keys[3]) {
|
|
||||||
dispatch(setZoomLevel(1));
|
|
||||||
}
|
|
||||||
if (key === keys[4]) {
|
|
||||||
dispatch(setZoomLevel(1.2));
|
|
||||||
}
|
|
||||||
if (key === keys[5]) {
|
|
||||||
dispatch(setZoomLevel(1.4));
|
|
||||||
}
|
|
||||||
if (key === keys[6]) {
|
|
||||||
dispatch(setZoomLevel(1.6));
|
|
||||||
}
|
|
||||||
if (key === keys[7]) {
|
|
||||||
dispatch(setZoomLevel(1.8));
|
|
||||||
}
|
|
||||||
if (key === keys[8]) {
|
|
||||||
dispatch(setZoomLevel(2));
|
|
||||||
}
|
|
||||||
if (key === keys[9]) {
|
|
||||||
dispatch(setZoomLevel(2.2));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+6
-6
@@ -6,25 +6,25 @@ export const showSpinner = () => ({ type: SHOW_SPINNER });
|
|||||||
export const hideSpinner = () => ({ type: HIDE_SPINNER });
|
export const hideSpinner = () => ({ type: HIDE_SPINNER });
|
||||||
export const toggleSpinner = () => ({ type: TOGGLE_SPINNER });
|
export const toggleSpinner = () => ({ type: TOGGLE_SPINNER });
|
||||||
|
|
||||||
export const reducers = (ui = {}, action) => {
|
export const reducers = (state = {}, action) => {
|
||||||
if (action.type === SHOW_SPINNER) {
|
if (action.type === SHOW_SPINNER) {
|
||||||
return {
|
return {
|
||||||
...ui,
|
...state,
|
||||||
spinner: true
|
spinner: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action.type === HIDE_SPINNER) {
|
if (action.type === HIDE_SPINNER) {
|
||||||
return {
|
return {
|
||||||
...ui,
|
...state,
|
||||||
spinner: false
|
spinner: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action.type === TOGGLE_SPINNER) {
|
if (action.type === TOGGLE_SPINNER) {
|
||||||
return {
|
return {
|
||||||
...ui,
|
...state,
|
||||||
spinner: !ui.spinner
|
spinner: !state.spinner
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return ui;
|
return state;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user