Browse Source

feat: Use emotion for css

v2.0.0rc
Dale 10 months ago
parent
commit
28be90246c
Signed by: Deiru GPG Key ID: AA250C0277B927E1
  1. 1
      .gitignore
  2. 6
      package.json
  3. 17
      src/assets/style-vars.ts
  4. 50
      src/components/App.tsx
  5. 24
      src/components/Dropdown.module.css
  6. 28
      src/components/Dropdown.styles.ts
  7. 4
      src/components/Dropdown.tsx
  8. 60
      src/components/Menu.module.css
  9. 58
      src/components/Menu.styles.ts
  10. 6
      src/components/Menu.tsx
  11. 22
      src/components/Playback.module.css
  12. 27
      src/components/Playback.styles.ts
  13. 10
      src/components/Playback.tsx
  14. 38
      src/components/Renderer/Image.styles.ts
  15. 2
      src/components/Renderer/Image.tsx
  16. 8
      src/components/Renderer/index.tsx
  17. 265
      src/components/Renderer/style.module.css
  18. 224
      src/components/Renderer/styles.ts
  19. 162
      src/components/SkillTree.module.css
  20. 156
      src/components/SkillTree.styles.ts
  21. 6
      src/components/SkillTree.tsx
  22. 18
      src/engine/Engine.ts
  23. 3
      src/engine/index.ts
  24. 2
      src/engine/lib/store.ts
  25. 0
      src/engine/lib/utils.ts
  26. 5
      src/engine/types.ts
  27. 41
      src/index.css
  28. 5
      src/index.tsx
  29. 3
      tsconfig.json
  30. 437
      yarn.lock

1
.gitignore

@ -6,6 +6,7 @@
.idea
*.log
tmp/
lib/
*.tern-port
node_modules/

6
package.json

@ -15,15 +15,18 @@
"build": "./node_modules/.bin/webpack --config webpack.config.js"
},
"dependencies": {
"@emotion/core": "^10.0.35",
"classnames": "^2.2.6",
"debounce": "^1.2.0",
"emotion": "^10.0.27",
"ramda": "^0.27.1",
"react": "^16.13.1",
"react-click-outside-hook": "^1.1.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.1",
"react-toastify": "^6.0.8",
"redux": "^4.0.5"
"redux": "^4.0.5",
"styled-components": "^5.2.0"
},
"devDependencies": {
"@types/classnames": "^2.2.10",
@ -33,6 +36,7 @@
"@types/react-click-outside-hook": "^1.0.0",
"@types/react-dom": "^16.9.8",
"@types/react-redux": "^7.1.9",
"@types/styled-components": "^5.1.3",
"css-loader": "^4.2.2",
"file-loader": "^6.1.0",
"html-webpack-plugin": "^4.4.1",

17
src/assets/style-vars.ts

@ -0,0 +1,17 @@
export const MainBgColor = 'rgb(243, 223, 193)';
export const MainBgColorDark = 'rgb(144, 87, 18)';
export const MainTextColor = 'rgb(26, 28, 26)';
export const MainErrorColor = 'rgb(232, 91, 30)';
export const MainSuccessColor = 'rgb(123, 189, 70)';
export const colors = {
MainBgColor,
MainBgColorDark,
MainTextColor,
MainErrorColor,
MainSuccessColor,
};
export default {
colors,
};

50
src/components/App.tsx

@ -1,19 +1,45 @@
import React, { useMemo } from 'react';
import { Global, css } from '@emotion/core';
import styled from 'styled-components';
import { colors } from '../assets/style-vars'
import Playback from './Playback';
import { Renderer } from './Renderer';
import createEngine from '@/engine';
import { Engine } from '@/engine/Engine';
import createEngine from '../engine';
import { Engine } from '../engine/Engine';
import { Provider } from 'react-redux';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'
import { EngineConfig } from '@/engine/types';
import { EngineConfig } from '../engine/types';
type AppProps<GS = object, MT = undefined, ST extends string = string> = {
config: EngineConfig<GS, MT, ST>
}
const StyledToastContainer = styled(ToastContainer)`
.Toastify__toast {
background: ${colors.MainBgColor};
color: ${colors.MainTextColor};
}
.Toastify__progress-bar--default {
background: ${colors.MainBgColorDark};
}
.Toastify__progress-bar--success {
background: ${colors.MainSuccessColor};
}
.Toastify__progress-bar--error {
background: ${colors.MainErrorColor};
}
.Toastify__close-button {
color: ${colors.MainTextColor};
}
`;
export default <GS, MT, ST extends string, >({ config }: AppProps<GS, MT, ST>) => {
const engine: Engine<GS, MT, ST> = useMemo(
() => createEngine<GS, MT, ST>(config, config.reducer),
@ -22,6 +48,20 @@ export default <GS, MT, ST extends string, >({ config }: AppProps<GS, MT, ST>) =
return (
<>
<Global styles={css`
@font-face {
font-family: 'Antic Slab Regular';
src: url('./assets/fonts/AnticSlab-Regular.woff2') format('woff2');
}
body {
font-family: "Antic Slab Regular";
font-size: 1.5rem;
color: ${colors.MainTextColor};
background-color: ${colors.MainBgColor};
font-weight: 400;
}
`} />
<Provider store={engine.store}>
<Playback engine={engine} engineConfig={config}>
{(_, config, backlog, location, time, next) => {
@ -29,7 +69,7 @@ export default <GS, MT, ST extends string, >({ config }: AppProps<GS, MT, ST>) =
}}
</Playback>
</Provider>
<ToastContainer />
<StyledToastContainer />
</>
);
}

24
src/components/Dropdown.module.css

@ -1,24 +0,0 @@
.dropdownContainer {
position: relative;
}
.dropdownContent {
display: flex;
top: calc(100% + 5px);
position: absolute;
border: 2px solid var(--MainBgColorDark);
background-color: var(--MainBgColor);
box-sizing: border-box;
transition: all 0.3s ease-in;
padding: 5px;
opacity: 0;
z-index: 1;
}
.activateContainer {
cursor: pointer;
}
.dropdownContentOpen {
opacity: 1;
transition: all 0.3s ease-in;
}

28
src/components/Dropdown.styles.ts

@ -0,0 +1,28 @@
import { css, cx } from 'emotion';
import { colors } from '../assets/style-vars';
export const dropdownContainer = css`
position: relative;
`;
export const dropdownContent = css`
display: flex;
top: calc(100% + 5px);
position: absolute;
border: 2px solid ${colors.MainBgColorDark};
background-color: ${colors.MainBgColor};
box-sizing: border-box;
transition: all 0.3s ease-in;
padding: 5px;
opacity: 0;
z-index: 1;
`
export const activateContainer = css`
cursor: pointer;
`;
export const dropdownContentOpen = css`
opacity: 1;
transition: all 0.3s ease-in;
`;

4
src/components/Dropdown.tsx

@ -1,7 +1,7 @@
import React, { FC, useState, useEffect } from 'react';
import React, { FC, useState } from 'react';
import cn from 'classnames';
import styles from './Dropdown.module.css';
import * as styles from './Dropdown.styles';
import { useClickOutside } from 'react-click-outside-hook';
type ActivateFn = (toggleDropdown: () => void, label?: string, isOpen?: boolean) => React.ReactNode;

60
src/components/Menu.module.css

@ -1,60 +0,0 @@
.menuContainer {
}
.menuItem {
font-weight: bold;
position: relative;
padding: 0.75em 15px 0.80em 15px;
border: none;
display: block;
width: 100%;
text-align: left;
font-family: inherit;
background-color: transparent;
}
.menuItem.start {
font-size: 1.5em;
}
.menuItem.load {
font-size: 1.17em;
}
.menuItem::after {
content: '';
height: 100%;
position: absolute;
background-color: var(--MainBgColorDark);
width: 0;
z-index: -1;
transition: width 0.1s ease-out;
top: 0;
left: 0;
}
.menuItem:hover {
color: var(--MainBgColor);
cursor: pointer;
}
.menuItem:hover::after {
width: 100%;
transition: width 0.3s ease-in;
}
.menuTitle {
padding: 15px;
border-bottom: 2px dashed var(--MainBgColorDark);
}
.menuTitle > h1 {
margin: 0;
}
.menuTitle > h1 > small {
font-size: 1.4rem;
display: inline-block;
}

58
src/components/Menu.styles.ts

@ -0,0 +1,58 @@
import { css } from 'emotion';
import { colors } from '../assets/style-vars';
const menuItem = css`
font-weight: bold;
position: relative;
padding: 0.75em 15px 0.80em 15px;
border: none;
display: block;
width: 100%;
text-align: left;
font-family: inherit;
background-color: transparent;
&.start {
font-size: 1.5em;
}
&.load {
font-size: 1.17em;
}
&:hover {
color: ${colors.MainBgColor};
cursor: pointer;
}
&::after {
content: '';
height: 100%;
position: absolute;
background-color: ${colors.MainBgColorDark};
width: 0;
z-index: -1;
transition: width 0.1s ease-out;
top: 0;
left: 0;
}
&:hover::after {
width: 100%;
transition: width 0.3s ease-in;
}
`;
const menuTitle = css`
padding: 15px;
border-bottom: 2px dashed ${colors.MainBgColorDark};
& > h1 {
margin: 0;
}
& > h1 > small {
font-size: 1.4rem;
display: inline-block;
}
`;
export default {
menuItem,
menuTitle,
start: 'start',
load: 'load',
}

6
src/components/Menu.tsx

@ -1,7 +1,7 @@
import React, { FC } from 'react';
import cn from 'classnames';
import styles from './Menu.module.css';
import styles from './Menu.styles';
type Props = {
startGame: () => void;
@ -9,7 +9,7 @@ type Props = {
}
const Menu: FC<Props> = ({ startGame, loadGame }) => (
<div className={styles.menuContainer}>
<>
<div className={styles.menuTitle}>
<h1>
Discoteque Demo <br />
@ -18,7 +18,7 @@ const Menu: FC<Props> = ({ startGame, loadGame }) => (
</div>
<button className={cn(styles.menuItem, styles.start)} onClick={startGame}>Start Game</button>
<button className={cn(styles.menuItem, styles.load)} onClick={loadGame}>Load Game</button>
</div>
</>
);
export default Menu;

22
src/components/Playback.module.css

@ -1,22 +0,0 @@
.playbackContainer {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
}
.playbackRestart {
color: var(--MainBgColorDark);
cursor: pointer;
}
.playbackRestart:hover {
text-decoration: underline;
}
@media(max-width: 799px) {
.playbackRestart {
margin-bottom: 50px;
}
}

27
src/components/Playback.styles.ts

@ -0,0 +1,27 @@
import { css } from 'emotion';
import { colors } from '../assets/style-vars';
const playbackContainer = css`
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
`;
const playbackRestart = css`
color: ${colors.MainBgColorDark};
cursor: pointer;
@media (min-width: 799px) {
margin-bottom: 50px;
}
&:hover {
text-decoration: underline;
}
`;
export default {
playbackContainer,
playbackRestart,
}

10
src/components/Playback.tsx

@ -1,12 +1,12 @@
import React, { useEffect } from 'react';
import React from 'react';
import * as R from 'ramda';
import { Engine } from '@/engine/Engine';
import { Engine } from '../engine/Engine';
import styles from '@/components/Playback.module.css';
import { ILine, ILineOption, Chrono, ILocation, EngineConfig } from '@/engine/types';
import styles from '../components/Playback.styles';
import { ILine, ILineOption, Chrono, ILocation, EngineConfig } from '../engine/types';
import { useSelector, useDispatch } from 'react-redux';
import { setState, stateSelector } from '@/engine/lib/store';
import { setState, stateSelector } from '../engine/lib/store';
type PlaybackRenderFn<GS = any, MT = undefined, ST extends string = string> = (
engine: Engine<GS, MT, ST>,

38
src/components/Renderer/Image.module.css → src/components/Renderer/Image.styles.ts

@ -1,36 +1,34 @@
.imageContainer {
import { css, cx } from 'emotion';
export const imageContainer = css`
height: 10rem;
position: absolute;
box-sizing: border-box;
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
@media(max-width: 1099px) {
display: none;
}
& > div {
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
`;
.imageContainerHidden {
export const imageContainerHidden = css`
border: 0;
transition: all 0.2s ease-in;
width: 0;
left: -4px;
}
`;
.imageContainerVisible {
export const imageContainerVisible = css`
transition: all 0.2s ease-out;
border: 4px solid black;
width: 8rem;
top: 1rem;
left: -8rem;
}
.imageContainer > div {
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
@media(max-width: 1099px) {
.imageContainer {
display: none;
}
}
`;

2
src/components/Renderer/Image.tsx

@ -1,6 +1,6 @@
import React, { FC } from 'react';
import styles from './Image.module.css';
import * as styles from './Image.styles';
import cn from 'classnames';

8
src/components/Renderer/index.tsx

@ -2,11 +2,11 @@ import React from 'react';
import * as R from 'ramda';
import { useSelector, useDispatch } from 'react-redux';
import styles from './style.module.css';
import { stateSelector as engineStateSelector, gameStateSelector, setState, initState, resetState } from '@/engine/lib/store';
import { IActor, ILine, ILineOption, ILocation, EngineState, Chrono, EngineConfig } from '@/engine/types';
import * as styles from './styles';
import { stateSelector as engineStateSelector, gameStateSelector, setState, initState, resetState } from '../../engine/lib/store';
import { IActor, ILine, ILineOption, ILocation, EngineState, Chrono, EngineConfig } from '../../engine/types';
import { toast } from 'react-toastify';
import SkillTree from '@/components/SkillTree';
import SkillTree from '../../components/SkillTree';
import Image from './Image';
import Dropdown from '../Dropdown';

265
src/components/Renderer/style.module.css

@ -1,265 +0,0 @@
.rendererContainer {
width: 100%;
max-width: 800px;
max-height: 600px;
height: 100%;
display: flex;
flex-direction: column;
border-radius: 6px;
position: relative;
border: 4px solid var(--MainTextColor);
box-sizing: border-box;
}
.textContainer {
display: flex;
height: 100%;
overflow-y: auto;
}
.textList {
list-style: none;
margin: 0;
padding-left: 0;
margin: 15px;
}
.textList > li::before {
content: '>';
display: inline-block;
width: 40px;
text-align: center;
}
.textList > li {
position: relative;
}
.textList > li::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
height: 100%;
width: 100%;
background-color: var(--MainBgColor);
animation: lineSlide 0.6s normal forwards linear;
animation-iteration-count: 1;
}
@keyframes lineSlide {
from {
width: 100%;
}
to {
width: 0;
}
}
@keyframes lineSlideSuccess {
from {
width: 100%;
color: initial;
}
50% {
color: var(--MainSuccessColor);
}
to {
width: 0;
color: initial;
}
}
@keyframes lineSlideFail {
from {
width: 100%;
color: initial;
}
50% {
color: var(--MainFailColor);
}
to {
width: 0;
color: initial;
}
}
.textList > li:last-of-type {
margin-bottom: 15px;
}
.optionsContainer {
display: flex;
height: 200px;
overflow-y: auto;
align-items: center;
border-top: 2px dashed var(--MainBgColorDark);
padding: 0;
}
.optionsList {
margin: 0;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.optionsList > div {
width: 100%;
cursor: pointer;
position: relative;
transition: color 0.4s linear;
box-sizing: border-box;
border-bottom: 2px solid var(--MainBgColorDark);
padding: 5px 15px 5px 15px;
}
.optionsList > div:last-of-type {
border-bottom: 0;
}
.optionsList > div::before {
content: '';
background-color: var(--MainBgColorDark);
position: absolute;
top: 0;
left: 0;
display: inline-block;
z-index: -1;
width: 0;
height: 100%;
transition: width 0.4s linear;
}
.optionsList > div:hover {
color: var(--MainBgColor);
}
.optionsList > div:hover::before {
width: 100%;
}
.optionsNext {
height: 100%;
width: 100%;
display: flex;
color: var(--MainBgColorDark);
font-weight: bold;
letter-spacing: 10px;
text-transform: uppercase;
font-size: 2rem;
justify-content: center;
align-items: center;
cursor: pointer;
user-select: none;
transition: border 0.3s ease-in;
position: relative;
overflow: hidden;
text-align: center;
}
.optionsNext:after {
content: "";
background: var(--MainBgColorDark);
display: block;
position: absolute;
padding-top: 300%;
padding-left: 350%;
margin-left: -20px!important;
margin-top: -120%;
opacity: 0;
transition: all 1s;
}
.optionsNext:focus {
outline: none;
}
.optionsNext:active:after {
padding: 0;
margin: 0;
opacity: 1;
transition: 0s
}
.toolbarContainer {
width: 100%;
border-bottom: 2px dashed var(--MainBgColorDark);
padding: 15px;
position: relative;
box-sizing: border-box;
justify-content: space-between;
display: flex;
min-height: 30px;
}
.toolbarTools {
align-self: flex-start;
border-right: 1px solid var(--MainBgColorDark);
margin-right: 5px;
padding-right: 2px;
height: 100%;
display: inline-flex;
align-items: center;
}
.toolbarTools > .toolbarButtons {
display: flex;
flex-direction: column;
}
.toolbarButtons > .toolbarSpacer {
display: block;
height: 35px;
background-color: var(--MainBgColorDark);
border: 2px dashed var(--MainBgColorDark);
box-sizing: border-box;
magrin: 10px 0 10px 0;
}
.toolbarStatus {
align-self: flex-end;
text-align: left;
border-right: 1px solid var(--MainBgColorDark);
margin-right: 5px;
padding-right: 2px;
height: 100%;
display: inline-flex;
align-items: center;
flex: 2;
}
.toolbarTime {
text-align: right;
height: 100%;
display: inline-flex;
align-items: center;
}
.toolbarButtons > span {
display: inline-flex;
width: 100%;
transition: all 0.2s linear;
cursor: pointer;
border-bottom: 1px solid var(--MainBgColorDark);
}
.toolbarButtons > span:last-of-type {
border-bottom: 0;
}
.toolbarButtons > span:hover {
background-color: var(--MainBgColorDark);
color: var(--MainBgColor);
}
@media(max-width: 799px) {
.rendererContainer {
max-height: unset;
border-radius: 0;
}
.optionsList > div {
padding-top: 15px;
padding-bottom: 15px;
}
}

224
src/components/Renderer/styles.ts

@ -0,0 +1,224 @@
import { css, keyframes } from 'emotion';
import { colors } from '../../assets/style-vars';
export const rendererContainer = css`
width: 100%;
max-width: 800px;
max-height: 600px;
height: 100%;
display: flex;
flex-direction: column;
border-radius: 6px;
position: relative;
border: 4px solid ${colors.MainTextColor};
box-sizing: border-box;
@media(max-width: 799px) {
max-height: unset;
border-radius: 0;
}
`;
export const textContainer = css`
display: flex;
height: 100%;
overflow-y: auto;
`;
const lineSlide = keyframes`
from {
width: 100%;
}
to {
width: 0;
}
`;
export const textList = css`
list-style: none;
margin: 0;
padding-left: 0;
margin: 15px;
& > li {
position: relative;
&::before {
content: '>';
display: inline-block;
width: 40px;
text-align: center;
}
&::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
height: 100%;
width: 100%;
background-color: ${colors.MainBgColor};
animation: ${lineSlide} 0.6s normal forwards linear;
animation-iteration-count: 1;
}
&:last-of-type {
margin-bottom: 15px;
}
}
`
export const optionsContainer = css`
display: flex;
height: 200px;
overflow-y: auto;
align-items: center;
border-top: 2px dashed ${colors.MainBgColorDark};
padding: 0;
`
export const optionsList = css`
margin: 0;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
& > div {
width: 100%;
cursor: pointer;
position: relative;
transition: color 0.4s linear;
box-sizing: border-box;
border-bottom: 2px solid ${colors.MainBgColorDark};
padding: 5px 15px 5px 15px;
@media(max-width: 799px) {
padding-top: 15px;
padding-bottom: 15px;
}
&:last-of-type {
border-bottom: 0;
}
&:hover {
color: ${colors.MainBgColor};
}
&::before {
content: '';
background-color: ${colors.MainBgColorDark};
position: absolute;
top: 0;
left: 0;
display: inline-block;
z-index: -1;
width: 0;
height: 100%;
transition: width 0.4s linear;
}
&:hover::before {
width: 100%;
}
}
`;
export const optionsNext = css`
height: 100%;
width: 100%;
display: flex;
color: ${colors.MainBgColorDark};
font-weight: bold;
letter-spacing: 10px;
text-transform: uppercase;
font-size: 2rem;
justify-content: center;
align-items: center;
cursor: pointer;
user-select: none;
transition: border 0.3s ease-in;
position: relative;
overflow: hidden;
text-align: center;
&::after {
content: "";
background: ${colors.MainBgColorDark};
display: block;
position: absolute;
padding-top: 300%;
padding-left: 350%;
margin-left: -20px!important;
margin-top: -120%;
opacity: 0;
transition: all 1s;
}
&:focus {
outline: none;
}
&:active::after {
padding: 0;
margin: 0;
opacity: 1;
transition: 0s
}
`;
export const toolbarContainer = css`
width: 100%;
border-bottom: 2px dashed ${colors.MainBgColorDark};
padding: 15px;
position: relative;
box-sizing: border-box;
justify-content: space-between;
display: flex;
min-height: 30px;
`;
export const toolbarTools = css`
align-self: flex-start;
border-right: 1px solid ${colors.MainBgColorDark};
margin-right: 5px;
padding-right: 2px;
height: 100%;
display: inline-flex;
align-items: center;
& > .toolbarButtons {
display: flex;
flex-direction: column;
& > .toolbarSpacer {
display: block;
height: 35px;
background-color: ${colors.MainBgColorDark};
border: 2px dashed ${colors.MainBgColorDark};
box-sizing: border-box;
magrin: 10px 0 10px 0;
}
& > span {
display: inline-flex;
width: 100%;
transition: all 0.2s linear;
cursor: pointer;
border-bottom: 1px solid ${colors.MainBgColorDark};
&:last-of-type {
border-bottom: 0;
}
&:hover {
background-color: ${colors.MainBgColorDark};
color: ${colors.MainBgColor};
}
}
}
`;
export const toolbarButtons = 'toolbarButtons';
export const toolbarSpacer = 'toolbarSpacer';
export const toolbarStatus = css`
align-self: flex-end;
text-align: left;
border-right: 1px solid ${colors.MainBgColorDark};
margin-right: 5px;
padding-right: 2px;
height: 100%;
display: inline-flex;
align-items: center;
flex: 2;
`;
export const toolbarTime = css`
text-align: right;
height: 100%;
display: inline-flex;
align-items: center;
`;

162
src/components/SkillTree.module.css

@ -1,162 +0,0 @@
.skillList {
list-style: none;
padding: 0;
margin: 0;
}
.skillTree {
display: flex;
flex-direction: column;
height: 100%;
}
.skillItem {
width: 100%;
display: flex;
justify-content: space-between;
font-size: 2.5rem;
padding: 10px 15px 10px 15px;
border-bottom: 1px solid black;
margin-bottom: 10px;
box-sizing: border-box;
flex-direction: column;
}
.skillDesc {
box-sizing: border-box;
font-size: 1.4rem;
transition: all 0.4s linear;
padding: 10px 0 0 0;
}
.skillDescClosed {
height: 0;
opacity: 0;
transition: all 0.4s linear;
}
.skillDescOpen {
height: 100%;
opacity: 1;
transition: all 0.4s linear;
}
.skillItem:last-of-type {
margin-bottom: 0;
border-bottom: 0;
}
.skillRow {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.skillButton {
font-size: 2.5rem;
width: 3.5rem;
background: transparent;
border: 1px solid black;
border-radius: 6px;
cursor: pointer;
color: var(--MainBgColorDark);
transition: all 0.3s ease-in;
}
.skillButton:hover {
background-color: var(--MainBgColorDark);
color: var(--MainBgColor);
transition: all 0.3s ease-out;
}
.skillButton.disabled {
cursor: default;
color: black;
border: none;
opacity: 0;
}
.skillButton:focus {
outline: none;
}
.skillValue {
margin-left: 10px;
margin-right: 10px;
width: 2rem;
text-align: center;
display: inline-block;
color: var(--MainBgColorDark);
}
.skillHeader {
display: flex;
justify-content: space-between;
border-bottom: 2px dashed var(--MainBgColorDark);
padding: 15px;
margin-bottom: 0.67rem;
}
/* 0.67 */
.skillHeader > h1 {
margin: 0;
}
.skillHeader > h1 > small {
font-size: 1.3rem;
display: inline-block;
}
.skillHeader > div {
display: flex;
align-items: center;
font-size: 1.8rem;
}
.skillName {
text-transform: capitalize;
}
.skillNameClick {
cursor: pointer;
color: var(--MainBgColorDark);
text-decoration: underline;
}
.skillConfirm {
font-size: 2.5rem;
letter-spacing: 10px;
background: transparent;
font-family: inherit;
font-weight: bold;
cursor: pointer;
border: none;
border-top: 2px dashed var(--MainBgColorDark);
width: 100%;
text-align: center;
color: var(--MainBgColorDark);
margin-top: auto;
padding: 15px;
transition: all 0.3s ease-in;
}
.skillConfirm:hover {
background-color: var(--MainBgColorDark);
color: var(--MainBgColor);
transition: all 0.3s ease-out;
}
@media(max-width: 799px) {
.skillName {
font-size: 1.8rem;
}
.skillValue {
font-size: 2rem;
margin: 0;
}
.skillButton {
font-size: 2rem;
width: 2.5rem;
}
}

156
src/components/SkillTree.styles.ts

@ -0,0 +1,156 @@
import { css } from 'emotion';
import { colors } from '../assets/style-vars';
export const skillList = css`
list-style: none;
padding: 0;
margin: 0;
`;
export const skillTree = css`
display: flex;
flex-direction: column;
height: 100%;
`;
export const skillItem = css`
width: 100%;
display: flex;
justify-content: space-between;
font-size: 2.5rem;
padding: 10px 15px 10px 15px;
border-bottom: 1px solid black;
margin-bottom: 10px;
box-sizing: border-box;
flex-direction: column;
&:last-of-type {
margin-bottom: 0;
border-bottom: 0;
}
`;
export const skillDesc = css`
box-sizing: border-box;
font-size: 1.4rem;
transition: all 0.4s linear;
padding: 10px 0 0 0;
`;
export const skillDescClosed = css`
height: 0;
opacity: 0;
transition: all 0.4s linear;
`;
export const skillDescOpen = css`
height: 100%;
opacity: 1;
transition: all 0.4s linear;
`;
export const skillRow = css`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
`;
export const skillButton = css`
font-size: 2.5rem;
width: 3.5rem;
background: transparent;
border: 1px solid black;
border-radius: 6px;
cursor: pointer;
color: ${colors.MainBgColorDark};
transition: all 0.3s ease-in;
&:hover {
background-color: ${colors.MainBgColorDark};
color: ${colors.MainBgColor};
transition: all 0.3s ease-out;
}
&.disabled {
cursor: default;
color: black;
border: none;
opacity: 0;
}
&:focus {
outline: none;
}
@media(max-width: 799px) {
font-size: 2rem;
width: 2.5rem;
}
`;
export const skillValue = css`
margin-left: 10px;
margin-right: 10px;
width: 2rem;
text-align: center;
display: inline-block;
color: ${colors.MainBgColorDark};
@media(max-width: 799px) {
font-size: 2rem;
margin: 0;
}
`;
export const skillHeader = css`
display: flex;
justify-content: space-between;
border-bottom: 2px dashed ${colors.MainBgColorDark};
padding: 15px;
margin-bottom: 0.67rem;
& > h1 {
margin: 0;
& > small {
font-size: 1.3rem;
display: inline-block;
}
}
& > div {
display: flex;
align-items: center;
font-size: 1.8rem;
}
}
`;
export const skillName = css`
text-transform: capitalize;
@media(max-width: 799px) {
font-size: 1.8rem;
}
`;
export const skillNameClick = css`
cursor: pointer;
color: ${colors.MainBgColorDark};
text-decoration: underline;
`;
export const skillConfirm = css`
font-size: 2.5rem;
letter-spacing: 10px;
background: transparent;
font-family: inherit;
font-weight: bold;
cursor: pointer;
border: none;
border-top: 2px dashed ${colors.MainBgColorDark};
width: 100%;
text-align: center;
color: $(colors.MainBgColorDark);
margin-top: auto;
padding: 15px;
transition: all 0.3s ease-in;
&:hover {
background-color: ${colors.MainBgColorDark};
color: ${colors.MainBgColor};
transition: all 0.3s ease-out;
}
`;
export const disabled = 'disabled';

6
src/components/SkillTree.tsx

@ -1,10 +1,10 @@
import React, { FC, useState, useMemo } from 'react';
import * as R from 'ramda';
import styles from './SkillTree.module.css';
import * as styles from './SkillTree.styles';
import { useDispatch, useSelector } from 'react-redux';
import { stateSelector, setSkill, setSkillpoints, setState } from '@/engine/lib/store';
import { EngineState } from '@/engine/types';
import { stateSelector, setSkill, setSkillpoints, setState } from '../engine/lib/store';
import { EngineState } from '../engine/types';
import cn from 'classnames';

18
src/engine/Engine.ts

@ -1,5 +1,5 @@
import * as R from 'ramda';
import { ILineOption, EngineState, ILineRaw, ILine, INode, LineFn, IActor, ILocation, Chrono, DayCycleFn } from './types';
import { ILineOption, EngineState, ILineRaw, ILine, INode, LineFn, IActor, ILocation, Chrono, EngineConfig } from './types';
import makeStore, * as actions from './lib/store';
import { Reducer, CombinedState, Store } from 'redux';
@ -13,9 +13,9 @@ const isLocation = <GS = object, MT = undefined>(node?: INode<GS, MT> | null): n
export class Engine<GS = object, MT = undefined, ST extends string = string> {
store: EngineStore<GS, MT, ST>;
onDayCycle: DayCycleFn;
public constructor(state: EngineState<GS, MT, ST>, gameReducer: Reducer, onDayCycle: DayCycleFn) {
this.onDayCycle = onDayCycle;
config: EngineConfig<GS, MT, ST>;
public constructor(state: EngineState<GS, MT, ST>, gameReducer: Reducer, config: EngineConfig<GS, MT, ST>) {
this.config = config;
this.store = makeStore(gameReducer) as EngineStore<GS, MT, ST>;
this.store.dispatch(actions.initEngineState(state))
const firstLine = this.currentLine || this.next(this.gameState);
@ -34,7 +34,7 @@ export class Engine<GS = object, MT = undefined, ST extends string = string> {
const gameState = this.gameState;
const line = R.pathOr<ILineRaw<GS, MT> | LineFn<GS, MT> | null>(null, [this.state.node, 'lines', this.state.line], this.state.nodeMap);
if (line && typeof line === 'function') {
return this.augmentLine(line(this.state, gameState, this.store.dispatch))
return this.augmentLine(line(this.state, gameState, this.store.dispatch, this.config))
}
return this.augmentLine(line);
}
@ -69,7 +69,7 @@ export class Engine<GS = object, MT = undefined, ST extends string = string> {
this.switchLocation(node);
}
if (typeof nextLine === 'function') {
const result = this.augmentLine(nextLine(this.state, gameState, this.store.dispatch));
const result = this.augmentLine(nextLine(this.state, gameState, this.store.dispatch, this.config));
const defaultTime = result?.actor ? 1 : 0;
this.store.dispatch(actions.advanceTime(result?.time || defaultTime));
const line = result
@ -90,12 +90,12 @@ export class Engine<GS = object, MT = undefined, ST extends string = string> {
this.store.dispatch(actions.advanceLine(line));
const nextLine = this.currentNode?.lines[line];
const chrono = this.currentTime;
if (chrono && chrono.time === 1440) {
const newChrono = this.onDayCycle(chrono);
if (chrono && chrono.time === 1440 && this.config.onDayCycle) {
const newChrono = this.config.onDayCycle(chrono);
this.store.dispatch(actions.setChrono(newChrono));
}
if (typeof nextLine === 'function') {
const line = this.augmentLine(nextLine(this.state, gameState, this.store.dispatch));
const line = this.augmentLine(nextLine(this.state, gameState, this.store.dispatch, this.config));
const defaultTime = line?.actor ? 1 : 0;
this.store.dispatch(actions.advanceTime(line?.time || defaultTime));
return line ? [line] : this.next(gameState);

3
src/engine/index.ts

@ -22,5 +22,6 @@ export default <GS = object, MT = undefined, ST extends string = string>(config:
isOver: false,
}
}
return new Engine<GS, MT, ST>(state, reducer, config.onDayCycle || defaultDayCycleFn);
const supplyConfig = R.merge(config, { onDayCycle: defaultDayCycleFn });
return new Engine<GS, MT, ST>(state, reducer, supplyConfig);
}

2
src/engine/lib/store.ts

@ -1,6 +1,6 @@
import * as R from 'ramda';
import { createStore, Reducer, combineReducers } from 'redux';
import { EngineState, Chrono } from '@/engine/types';
import { EngineState, Chrono } from 'discoteque/lib/engine/types';
type INIT = 'INIT';
type ENGINE_INIT = 'INIT-ENGINE';

0
src/engine/lib/utils.ts

5
src/engine/types.ts

@ -14,7 +14,10 @@ export interface ILineRaw<ST, MT = undefined> {
meta?: MT;
time?: number;
}
export type LineFn<ST = any, MT = undefined, SST extends string = string> = (engineState: EngineState<ST, MT, SST>, gameState: ST, gameDispatch: Dispatch<any>) => ILineRaw<ST, MT> | null | void;
export type LineFn<ST = any, MT = undefined, SST extends string = string, CF = EngineConfig<ST, MT, SST>> =
(engineState: EngineState<ST, MT, SST>, gameState: ST, gameDispatch: Dispatch<any>, config: CF) =>
ILineRaw<ST, MT> | null | void;
export type BuiltinNodeKind = 'node' | 'actor' | 'location';
export interface INode<GS = object, MT = undefined, KT = BuiltinNodeKind> {

41
src/index.css

@ -1,41 +0,0 @@
@font-face {
font-family: 'Antic Slab Regular';
src: url('./assets/fonts/AnticSlab-Regular.woff2') format('woff2');
}
body {
font-family: "Antic Slab Regular";
font-size: 1.5rem;
color: var(--MainTextColor);
background-color: var(--MainBgColor);
font-weight: 400;
}
:root {
--MainBgColor: rgb(243, 223, 193);
--MainBgColorDark: rgb(144, 87, 18);
--MainTextColor: rgb(26, 28, 26);
--MainErrorColor: rgb(232, 91, 30);
--MainSuccessColor: rgb(123, 189, 70);
}
.Toastify__toast {
background: var(--MainBgColor);
color: var(--MainTextColor);
}
.Toastify__progress-bar--default {
background: var(--MainBgColorDark);
}
.Toastify__progress-bar--success {
background: var(--MainSuccessColor);
}
.Toastify__progress-bar--error {
background: var(--MainErrorColor);
}
.Toastify__close-button {
color: var(--MainTextColor);
}

5
src/index.tsx

@ -1,10 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from '@/components/App';
import App from './components/App';
import '@/index.css';
import { EngineConfig } from '@/engine/types';
import { EngineConfig } from './engine/types';
function initApp<GS = object, MT = undefined, ST extends string = string>(config: EngineConfig<GS, MT, ST>) {
ReactDOM.render(<App<GS, MT, ST> config={config} />, document.getElementById('root'));

3
tsconfig.json

@ -21,10 +21,11 @@
"strictNullChecks": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
"discoteque/lib/*": ["src/*"]
},
"plugins": [
{

437
yarn.lock

@ -2,13 +2,202 @@
# yarn lockfile v1
"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
dependencies:
"@babel/highlight" "^7.10.4"
"@babel/generator@^7.11.5":
version "7.11.6"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620"
integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==
dependencies:
"@babel/types" "^7.11.5"
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.0.0":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3"
integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==
dependencies:
"@babel/types" "^7.10.4"
"@babel/helper-function-name@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
dependencies:
"@babel/helper-get-function-arity" "^7.10.4"
"@babel/template" "^7.10.4"
"@babel/types" "^7.10.4"
"@babel/helper-get-function-arity@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
dependencies: