aboutsummaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorLoek Le Blansch <32883851+lonkaars@users.noreply.github.com>2021-04-21 10:40:52 +0200
committerGitHub <noreply@github.com>2021-04-21 10:40:52 +0200
commitdadc722875b2095bd3d6c4ab628a644197b85f7b (patch)
tree9e061708fad5bfdcc40f4c40662d77fbc42cfe64 /components
parentc603cb79e7ba7fdbb101a506e36f6d8d70b3a8f4 (diff)
parent5cb39d822716c650e520c3855ef049ff308a348c (diff)
Merge pull request #12 from lonkaars/css-files
big redesign css move thing
Diffstat (limited to 'components')
-rw-r--r--components/account.tsx4
-rw-r--r--components/dialogBox.tsx29
-rw-r--r--components/footer.tsx4
-rw-r--r--components/gameBar.tsx92
-rw-r--r--components/gameSettings.tsx372
-rw-r--r--components/logo.tsx26
-rw-r--r--components/navbar.tsx78
-rw-r--r--components/notificationsArea.tsx103
-rw-r--r--components/page.tsx34
-rw-r--r--components/recentGames.tsx56
-rw-r--r--components/toast.tsx75
-rw-r--r--components/ui.tsx239
-rw-r--r--components/voerBord.tsx50
13 files changed, 275 insertions, 887 deletions
diff --git a/components/account.tsx b/components/account.tsx
index f24135f..cb44346 100644
--- a/components/account.tsx
+++ b/components/account.tsx
@@ -19,14 +19,12 @@ export function AccountAvatar(props: {
if (props.dummy) image = dummy;
return <div
+ className={'accountAvatar dispinbl ' + (props.round ? 'round' : '')}
style={{
width: props.size,
height: props.size,
backgroundColor: props.fallbackFill || 'var(--background)',
backgroundImage: `url(${image})`,
- backgroundSize: 'cover',
- display: 'inline-block',
- borderRadius: props.size / 2 * Number(props.round || 0),
}}
/>;
}
diff --git a/components/dialogBox.tsx b/components/dialogBox.tsx
index 7abbded..a5b02fa 100644
--- a/components/dialogBox.tsx
+++ b/components/dialogBox.tsx
@@ -1,4 +1,4 @@
-import { CSSProperties, ReactNode } from 'react';
+import { ReactNode } from 'react';
import { Vierkant } from './ui';
@@ -7,32 +7,17 @@ import CancelIcon from '@material-ui/icons/Cancel';
export function DialogBox(props: {
children: ReactNode;
title: string;
- style?: CSSProperties;
onclick?: () => void;
+ hidden?: boolean;
+ className?: string;
}) {
return <Vierkant
- style={{
- position: 'fixed',
- top: '50%',
- left: '50%',
- transform: 'translate(-50%, -50%)',
- boxShadow: '0 8px 32px -5px #0007',
- width: 392,
- ...props.style,
- }}
+ className={'dialogbox bg-800 drop-2 pad-l posfix abscenter ' + (props.hidden ? 'dispnone' : '') + ' '
+ + props.className}
>
- <h2 style={{ marginBottom: 24 }}>{props.title}</h2>
+ <h2 className='title'>{props.title}</h2>
<span onClick={props.onclick}>
- <CancelIcon
- style={{
- position: 'absolute',
- top: 25,
- right: 25,
- color: 'var(--text)',
- opacity: .85,
- cursor: 'pointer',
- }}
- />
+ <CancelIcon className='posabs close icon subtile' />
</span>
{props.children}
</Vierkant>;
diff --git a/components/footer.tsx b/components/footer.tsx
index 36ebef3..f39946c 100644
--- a/components/footer.tsx
+++ b/components/footer.tsx
@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
-import { LogoDark } from '../components/logo';
+import Logo from '../components/logo';
import ExitToAppOutlinedIcon from '@material-ui/icons/ExitToAppOutlined';
import ExtensionIcon from '@material-ui/icons/Extension';
@@ -25,7 +25,7 @@ function PageLink(props: {
export function Footer() {
return <div className='footer'>
<div className='header'>
- <LogoDark />
+ <Logo />
<h2>4 op een rij</h2>
</div>
<div className='content'>
diff --git a/components/gameBar.tsx b/components/gameBar.tsx
index 0d7d4d9..9a7ca59 100644
--- a/components/gameBar.tsx
+++ b/components/gameBar.tsx
@@ -1,5 +1,5 @@
-import { CSSProperties, ReactNode } from 'react';
-import { Bubble, Vierkant } from './ui';
+import { ReactNode } from 'react';
+import { Vierkant } from './ui';
import ExitToAppRoundedIcon from '@material-ui/icons/ExitToAppRounded';
import NavigateBeforeRoundedIcon from '@material-ui/icons/NavigateBeforeRounded';
@@ -9,61 +9,30 @@ import SettingsRoundedIcon from '@material-ui/icons/SettingsRounded';
function GameBarModule(props: {
children?: ReactNode;
onclick?: () => void;
+ className?: string;
}) {
return <Vierkant
- style={{
- backgroundColor: 'var(--background-alt)',
- padding: 12,
- borderRadius: 6,
- margin: 0,
- verticalAlign: 'top',
- cursor: props.onclick ? 'pointer' : 'default',
- }}
+ className={'gameBarButton bg-700 pad-m round-t valigntop ' + props.className + ' '
+ + (props.onclick ? 'cpointer' : '')}
onclick={props.onclick}
>
{props.children}
</Vierkant>;
}
-var GameBarSpacer = () => <div style={{ width: 8, display: 'inline-block' }}></div>;
-
-var GameBarAlignStyle: CSSProperties = {
- display: 'inline-block',
-};
-
export function GameBar(props: {
turn: boolean;
player1: boolean;
active: boolean;
resignFunction: () => void;
}) {
- return <Vierkant
- className='gameBar'
- style={{
- padding: 8,
- width: 'calc(100% - 12px)',
- }}
- >
- <div style={{ gridAutoColumns: 'auto' }}>
- <div style={{ ...GameBarAlignStyle, float: 'left' }}>
- <div
- style={{
- width: 32,
- height: 32,
- margin: 8,
- backgroundColor: props.turn ? 'var(--disk-b)' : 'var(--disk-a)',
- borderRadius: 16,
- display: 'inline-block',
- }}
- />
- <h2
- style={{
- fontSize: 20,
- margin: 12,
- verticalAlign: 'top',
- display: 'inline-block',
- }}
- >
+ return <Vierkant className='gameBar bg-800 w100m2m pad-s'>
+ <div>
+ <div className='floatl dispinbl'>
+ {props.active && <div
+ className={'move dispinbl ' + (props.turn ? 'move-a' : 'move-b')}
+ />}
+ <h2 className='pad-m valigntop dispinbl'>
{!props.active
? 'Wachten op tegenstander...'
: props.turn == props.player1
@@ -71,49 +40,22 @@ export function GameBar(props: {
: 'Tegenstander'}
</h2>
</div>
- <div
- style={{
- ...GameBarAlignStyle,
- position: 'absolute',
- top: '50%',
- left: '50%',
- transform: 'translate(-50%, -50%)',
- }}
- >
- <span
- style={{
- color: 'var(--text)',
- fontSize: 20,
- opacity: .75 - .75,
- }}
- >
- 0-0
- </span>
+ <div className='score winning dispnone subtile posabs abscenter'>
+ <span>0-0</span>
</div>
- <div style={{ ...GameBarAlignStyle, float: 'right' }}>
+ <div className='buttons floatr dispinbl'>
<GameBarModule>
<SettingsRoundedIcon />
</GameBarModule>
- <GameBarSpacer />
- <GameBarModule>
- <span
- style={{
- margin: '0 4px',
- fontSize: 20,
- }}
- >
- 00:00
- </span>
+ <GameBarModule className='timer nosel'>
+ <span>00:00</span>
</GameBarModule>
- <GameBarSpacer />
<GameBarModule onclick={props.resignFunction}>
<ExitToAppRoundedIcon />
</GameBarModule>
- <GameBarSpacer />
<GameBarModule>
<NavigateBeforeRoundedIcon />
</GameBarModule>
- <GameBarSpacer />
<GameBarModule>
<NavigateNextRoundedIcon />
</GameBarModule>
diff --git a/components/gameSettings.tsx b/components/gameSettings.tsx
index f562e5d..e2c60e6 100644
--- a/components/gameSettings.tsx
+++ b/components/gameSettings.tsx
@@ -1,5 +1,5 @@
import axios from 'axios';
-import { Component, CSSProperties, ReactNode } from 'react';
+import { ReactNode, useEffect, useState } from 'react';
import { ruleset, userPreferences } from '../api/api';
import { DialogBox } from './dialogBox';
@@ -7,106 +7,57 @@ import { Button, CheckBox, Input, Vierkant } from './ui';
import BuildOutlinedIcon from '@material-ui/icons/BuildOutlined';
-type CurrentGameSettingsStateType = {
- editGameRulesDialogVisible: boolean;
- ruleset: ruleset;
-};
-
-export class CurrentGameSettings extends Component {
- state: CurrentGameSettingsStateType = {
- editGameRulesDialogVisible: false,
- ruleset: {
- timelimit: {
- enabled: false,
- shared: false,
- },
- ranked: false,
+export function CurrentGameSettings() {
+ var [editGameRulesDialogVisible, setEditGameRulesDialogVisible] = useState(false);
+ var [ruleset, setRuleset] = useState<ruleset>({
+ timelimit: {
+ enabled: false,
+ shared: false,
+ minutes: 0,
+ seconds: 0,
+ addmove: 0,
},
- };
-
- constructor(props: {}) {
- super(props);
+ ranked: false,
+ });
- if (typeof window === 'undefined') return; // return if run on server
-
- axios.request<userPreferences>({
+ useEffect(() => {
+ axios.request<{ preferences: userPreferences; }>({
method: 'get',
url: `/api/user/preferences`,
headers: { 'content-type': 'application/json' },
})
- // FIXME: this assumes the request ruleset has all properties of a ruleset
- .then(request => this.setState({ ruleset: request.data.ruleset || this.state.ruleset }))
+ .then(request => setRuleset(request.data.preferences.ruleset))
.catch(() => {});
- }
-
- showEditGameRules = () => this.setState({ editGameRulesDialogVisible: true });
- hideEditGameRules = () => this.setState({ editGameRulesDialogVisible: false });
- setGameRules = (newRules: ruleset) => this.setState({ ruleset: newRules });
+ }, []);
- render() {
- var timelimit_str = this.state.ruleset.timelimit.enabled
- ? `${this.state.ruleset.timelimit.minutes}m${this.state.ruleset.timelimit.seconds}s plus ${this.state.ruleset.timelimit.addmove}`
- : 'Geen tijdslimiet';
- var ranked_str = this.state.ruleset.ranked
- ? 'Gerangschikt'
- : 'Niet gerangschikt';
- return <div
- style={{
- position: 'relative',
- height: 80,
- overflow: 'visible',
- zIndex: 1,
- }}
+ var timelimit_str = ruleset.timelimit.enabled
+ ? `${ruleset.timelimit.minutes}m${ruleset.timelimit.seconds}s plus ${ruleset.timelimit.addmove}`
+ : 'Geen tijdslimiet';
+ var ranked_str = ruleset.ranked
+ ? 'Gerangschikt'
+ : 'Niet gerangschikt';
+ return <div className='editGameSettings posrel'>
+ <p className='currentRules subtile nosel posabs l0'>
+ {timelimit_str}
+ <br />
+ {ranked_str}
+ </p>
+ <Button
+ className='posabs r0 pad-m'
+ onclick={() => setEditGameRulesDialogVisible(true)}
>
- <p
- style={{
- opacity: .75,
- fontStyle: 'italic',
- userSelect: 'none',
- position: 'absolute',
- top: '50%',
- left: 0,
- transform: 'translateY(-50%)',
- }}
- >
- {timelimit_str}
- <br />
- {ranked_str}
- </p>
- <Button
- style={{
- width: 150,
- position: 'absolute',
- top: '50%',
- right: 0,
- transform: 'translateY(-50%)',
- }}
- onclick={this.showEditGameRules}
- >
- <BuildOutlinedIcon style={{ fontSize: 48 }} />
- <span
- style={{
- fontWeight: 600,
- position: 'absolute',
- right: 24,
- top: '50%',
- width: 85,
- verticalAlign: 'middle',
- textAlign: 'center',
- transform: 'translateY(-50%)',
- userSelect: 'none',
- }}
- >
- Spelregels aanpassen
- </span>
- </Button>
- <EditGameSettings
- parentState={this.state}
- hideEditGameRules={this.hideEditGameRules}
- setGameRules={this.setGameRules}
- />
- </div>;
- }
+ <BuildOutlinedIcon className='icon' />
+ <span className='posabs center nosel text'>
+ Spelregels aanpassen
+ </span>
+ </Button>
+ <EditGameSettings
+ hideEditGameRules={() => setEditGameRulesDialogVisible(false)}
+ setGameRules={setRuleset}
+ ruleset={ruleset}
+ visible={editGameRulesDialogVisible}
+ />
+ </div>;
}
function GameSettingsSection(props: {
@@ -118,32 +69,17 @@ function GameSettingsSection(props: {
}) {
return <Vierkant
id={props.id}
- style={{
- backgroundColor: 'var(--background-alt)',
- width: '100%',
- padding: 16,
- margin: 0,
- marginBottom: props.noMarginBottom ? 0 : 24,
- }}
+ className='pad-m editableRulesSection'
>
- <span
- style={{
- verticalAlign: 'top',
- fontSize: 14,
- fontWeight: 600,
- }}
- >
+ <span className='valigntop nosel'>
{props.title}
</span>
- <CheckBox
- state={props.state}
- id={`${props.id}_enabled`}
- style={{
- verticalAlign: 'top',
- float: 'right',
- margin: -3,
- }}
- />
+ <div className='checkboxWrapper valigntop floatr'>
+ <CheckBox
+ state={props.state}
+ id={props.id + '_enabled'}
+ />
+ </div>
<div>{props.children}</div>
</Vierkant>;
}
@@ -151,137 +87,99 @@ function GameSettingsSection(props: {
function GameRule(props: {
title: string;
description: string;
- style?: CSSProperties;
}) {
- return <div
- style={{
- backgroundColor: 'var(--page-background)',
- borderRadius: 8,
- padding: '16px 0',
- textAlign: 'center',
- ...props.style,
- }}
- >
- <h1 style={{ color: 'var(--disk-a)', fontSize: 42 }}>{props.title}</h1>
- <p style={{ color: 'var(--text-alt)', maxWidth: 250, margin: '0 auto' }}>{props.description}</p>
+ return <div className='gamerule pad-m round-t center bg-900'>
+ <h1>{props.title}</h1>
+ <p>{props.description}</p>
</div>;
}
type editGameSettingsProps = {
- parentState: CurrentGameSettingsStateType;
+ visible: boolean;
+ ruleset: ruleset;
hideEditGameRules: () => void;
setGameRules: (newRules: ruleset) => void;
};
-export class EditGameSettings extends Component<editGameSettingsProps> {
- render() {
- return <DialogBox
- title='Spelregels aanpassen'
- style={{
- margin: 0,
- display: this.props.parentState.editGameRulesDialogVisible ? 'block' : 'none',
+export function EditGameSettings(props: editGameSettingsProps) {
+ return <DialogBox
+ title='Spelregels aanpassen'
+ hidden={!props.visible}
+ onclick={props.hideEditGameRules}
+ className='gameRuleEdit'
+ >
+ <div className='editableRules round-t'>
+ <GameSettingsSection
+ title='Tijdslimiet'
+ state={props.ruleset.timelimit.enabled}
+ id='timelimit'
+ >
+ <div className='sidebyside timeControls'>
+ <Input
+ id='timelimit_minutes'
+ type='number'
+ label='min'
+ min={0}
+ max={60}
+ value={props.ruleset.timelimit.minutes}
+ className='pad-m round-t'
+ />
+ <Input
+ id='timelimit_seconds'
+ type='number'
+ label='sec'
+ min={0}
+ max={60}
+ value={props.ruleset.timelimit.seconds}
+ className='pad-m round-t'
+ />
+ <Input
+ id='timelimit_addmove'
+ type='number'
+ label='plus'
+ min={0}
+ value={props.ruleset.timelimit.addmove}
+ className='pad-m round-t'
+ />
+ </div>
+ <CheckBox id='timelimit_shared' state={props.ruleset.timelimit.shared} />
+ <span className='valignsup'>
+ Timer gebruiken voor bijde spelers
+ </span>
+ </GameSettingsSection>
+ {false && <GameSettingsSection id='gamemodes' title='Regelset' state={false}>
+ <div className='sidebyside'>
+ <GameRule title='+2' description='Extra kolommen' />
+ <GameRule title='+4' description='Extra kolommen' />
+ </div>
+ <GameRule title='Gravity' description='De zwaartekracht draait soms' />
+ <GameRule title='Flashlight' description='Het veld wordt opgelicht door de vallende fiches' />
+ </GameSettingsSection>}
+ <GameSettingsSection
+ title='Gerangschikt spel'
+ state={props.ruleset.ranked}
+ id='ranked'
+ noMarginBottom
+ />
+ </div>
+ <Button
+ className='save'
+ onclick={() => {
+ var rules: ruleset = {
+ timelimit: {
+ enabled: document.getElementById('timelimit_enabled').classList.contains('on'),
+ minutes: Number((document.getElementById('timelimit_minutes') as HTMLInputElement).value),
+ seconds: Number((document.getElementById('timelimit_seconds') as HTMLInputElement).value),
+ addmove: Number((document.getElementById('timelimit_addmove') as HTMLInputElement).value),
+ shared: document.getElementById('timelimit_shared').classList.contains('on'),
+ },
+ ranked: document.getElementById('ranked_enabled').classList.contains('on'),
+ };
+ props.setGameRules(rules);
+ props.hideEditGameRules();
}}
- onclick={this.props.hideEditGameRules}
>
- <div
- style={{
- marginTop: 24,
- maxHeight: 500,
- overflowY: 'scroll',
- borderRadius: 8,
- }}
- >
- <GameSettingsSection
- title='Tijdslimiet'
- state={this.props.parentState.ruleset.timelimit.enabled}
- id='timelimit'
- >
- <div
- style={{
- display: 'grid',
- gridTemplateColumns: '1fr 1fr 1fr',
- gridGap: 16,
- margin: '16px 0',
- }}
- >
- <Input
- id='timelimit_minutes'
- type='number'
- label='min'
- min={0}
- max={60}
- value={this.props.parentState.ruleset.timelimit.minutes}
- />
- <Input
- id='timelimit_seconds'
- type='number'
- label='sec'
- min={0}
- max={60}
- value={this.props.parentState.ruleset.timelimit.seconds}
- />
- <Input
- id='timelimit_addmove'
- type='number'
- label='plus'
- min={0}
- value={this.props.parentState.ruleset.timelimit.addmove}
- />
- </div>
- <CheckBox id='timelimit_shared' state={this.props.parentState.ruleset.timelimit.shared} />
- <span
- style={{
- verticalAlign: 'super',
- marginLeft: 4,
- }}
- >
- Timer gebruiken voor bijde spelers
- </span>
- </GameSettingsSection>
- {false && <GameSettingsSection title='Regelset' state={false}>
- <div
- style={{
- display: 'grid',
- gridTemplateColumns: '1fr 1fr',
- gridGap: 16,
- margin: '16px 0',
- }}
- >
- <GameRule title='+2' description='Extra kolommen' />
- <GameRule title='+4' description='Extra kolommen' />
- </div>
- <GameRule style={{ marginBottom: 16 }} title='Gravity' description='De zwaartekracht draait soms' />
- <GameRule title='Flashlight' description='Het veld wordt opgelicht door de vallende fiches' />
- </GameSettingsSection>}
- <GameSettingsSection
- title='Gerangschikt spel'
- state={this.props.parentState.ruleset.ranked}
- id='ranked'
- noMarginBottom
- />
- </div>
- <Button
- style={{
- textAlign: 'center',
- marginTop: 24,
- }}
- onclick={() => {
- var rules: ruleset = {
- timelimit: {
- enabled: document.getElementById('timelimit_enabled').classList.contains('on'),
- minutes: Number((document.getElementById('timelimit_minutes') as HTMLInputElement).value),
- seconds: Number((document.getElementById('timelimit_seconds') as HTMLInputElement).value),
- addmove: Number((document.getElementById('timelimit_addmove') as HTMLInputElement).value),
- shared: document.getElementById('timelimit_shared').classList.contains('on'),
- },
- ranked: document.getElementById('ranked_enabled').classList.contains('on'),
- };
- this.props.setGameRules(rules);
- this.props.hideEditGameRules();
- }}
- >
- Instellingen opslaan
- </Button>
- </DialogBox>;
- }
+ Instellingen opslaan
+ </Button>
+ </DialogBox>;
}
diff --git a/components/logo.tsx b/components/logo.tsx
index e43aa88..df35370 100644
--- a/components/logo.tsx
+++ b/components/logo.tsx
@@ -1,26 +1,12 @@
-export function LogoDark() {
+export default function Logo() {
return (
<div className='noclick'>
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
- <rect width='24' height='24' fill='var(--background)' />
- <circle cx='6.5' cy='6.5' r='4.5' fill='var(--disk-b)' />
- <circle cx='6.5' cy='17.5' r='4.5' fill='var(--disk-a)' />
- <circle cx='17.5' cy='17.5' r='4.5' fill='var(--disk-b)' />
- <circle cx='17.5' cy='6.5' r='3.5' stroke='var(--text)' strokeWidth='2' />
- </svg>
- </div>
- );
-}
-
-export function LogoLight() {
- return (
- <div className='noclick'>
- <svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
- <rect width='24' height='24' fill='var(--page-background)' />
- <circle cx='6.5' cy='6.5' r='4.5' fill='var(--disk-b)' />
- <circle cx='6.5' cy='17.5' r='4.5' fill='var(--disk-a)' />
- <circle cx='17.5' cy='17.5' r='4.5' fill='var(--disk-b)' />
- <circle cx='17.5' cy='6.5' r='3.5' stroke='var(--background)' strokeWidth='2' />
+ <rect width='24' height='24' fill='none' />
+ <circle className='green' cx='6.5' cy='6.5' r='4.5' fill='var(--confirm)' />
+ <circle className='red' cx='6.5' cy='17.5' r='4.5' fill='var(--error)' />
+ <circle className='green' cx='17.5' cy='17.5' r='4.5' fill='var(--confirm)' />
+ <circle className='circle' cx='17.5' cy='6.5' r='3.5' stroke='var(--foreground)' strokeWidth='2' />
</svg>
</div>
);
diff --git a/components/navbar.tsx b/components/navbar.tsx
index 70de574..5ce43bb 100644
--- a/components/navbar.tsx
+++ b/components/navbar.tsx
@@ -1,8 +1,8 @@
import axios from 'axios';
-import { CSSProperties, useContext, useEffect, useState } from 'react';
+import { useContext, useEffect, useState } from 'react';
import { userInfo } from '../api/api';
-import { LogoDark } from '../components/logo';
+import Logo from '../components/logo';
import { AccountAvatar } from './account';
import { NotificationsArea } from './notificationsArea';
import { SocketContext } from './socketContext';
@@ -15,12 +15,6 @@ import SearchIcon from '@material-ui/icons/Search';
import SettingsIcon from '@material-ui/icons/Settings';
import VideogameAssetIcon from '@material-ui/icons/VideogameAsset';
-var NavBarItemStyle: CSSProperties = {
- margin: 12,
- marginBottom: 16,
- display: 'block',
-};
-
export function NavBar() {
var [loggedIn, setLoggedIn] = useState(false);
var [gotData, setGotData] = useState(false);
@@ -60,73 +54,31 @@ export function NavBar() {
})();
}, []);
- return <div
- className='navbar'
- style={{
- width: 48,
- height: '100%',
-
- lineHeight: 0,
-
- backgroundColor: 'var(--background)',
- display: 'inline-block',
-
- position: 'fixed',
- top: 0,
- left: 0,
-
- overflow: 'visible',
- whiteSpace: 'nowrap',
- zIndex: 2,
- }}
- >
- <div style={NavBarItemStyle}>
- <LogoDark />
+ return <div className='navbar bg-800 h100vh dispinbl t0 l0 posfix'>
+ <div className='item'>
+ <Logo />
</div>
- <a href='/' style={NavBarItemStyle}>
+ <a href='/' className='item'>
<Home />
</a>
- <a href='/game' style={NavBarItemStyle}>
+ <a href='/game' className='item'>
<VideogameAssetIcon />
</a>
- {false && <a href='/' style={NavBarItemStyle}>
+ {false && <a href='/' className='item'>
<ExtensionIcon />
</a>}
- <a href='/search' style={NavBarItemStyle}>
+ <a href='/search' className='item'>
<SearchIcon />
</a>
- <div
- style={{
- position: 'absolute',
- bottom: -4,
- left: 0,
- backgroundColor: 'var(--background)',
- }}
- >
- {loggedIn && <a
- style={{
- overflow: 'visible',
- position: 'relative',
- ...NavBarItemStyle,
- }}
- >
+ <div className='bg-800 bottomArea'>
+ {loggedIn && <a className='notifications item posrel'>
<div
- style={{ cursor: 'pointer' }}
+ className='iconWrapper'
onClick={() => setNotificationsAreaVisible(!notificationsAreaVisible)}
>
<NotificationsIcon />
- {gotNotifications && <div
- style={{
- backgroundColor: 'var(--disk-a)',
- width: 8,
- height: 8,
- borderRadius: 4,
- position: 'absolute',
- top: 2,
- right: 2,
- }}
- />}
+ {gotNotifications && <div className='notificationDot posabs' />}
</div>
<NotificationsArea
visible={notificationsAreaVisible}
@@ -134,12 +86,12 @@ export function NavBar() {
rerender={getNotifications}
/>
</a>}
- <a href={loggedIn ? '/user' : '/login'} style={NavBarItemStyle}>
+ <a href={loggedIn ? '/user' : '/login'} className='item'>
{loggedIn
? <AccountAvatar size={24} round />
: <PersonIcon />}
</a>
- {loggedIn && <a href='/settings' style={NavBarItemStyle}>
+ {loggedIn && <a href='/settings' className='item'>
<SettingsIcon />
</a>}
</div>
diff --git a/components/notificationsArea.tsx b/components/notificationsArea.tsx
index 9573b72..8ee554a 100644
--- a/components/notificationsArea.tsx
+++ b/components/notificationsArea.tsx
@@ -1,5 +1,5 @@
import axios from 'axios';
-import { CSSProperties, ReactNode, useContext, useEffect, useState } from 'react';
+import { ReactNode, useContext, useEffect, useState } from 'react';
import { gameInfo, userInfo } from '../api/api';
import { AccountAvatar } from './account';
@@ -35,52 +35,14 @@ export function NotificationsArea(props: {
setPreviousMessages(messages);
});
- return props.visible && <Bubble
- style={{
- left: 48 + 12,
- top: 92,
- transform: 'translateY(-100%)',
- textAlign: 'left',
- width: 400,
- height: 450,
- }}
- tuitjeStyle={{
- left: 12,
- bottom: 86,
- transform: 'translate(-100%, 100%) rotate(90deg)',
- }}
- >
- <h2 style={{ marginBottom: 24 }}>Meldingen</h2>
- <div
- style={{
- overflowY: 'scroll',
- whiteSpace: 'normal',
- height: 450 - 24 * 4,
- borderRadius: 6,
- }}
- >
+ return props.visible && <Bubble className='notificationsArea bg-700 pad-l'>
+ <h2 className='title'>Meldingen</h2>
+ <div className='inner round-t'>
{props.gameInvites?.map(game => <GameInvite hide={props.rerender} game={game} />)}
{props.friendRequests?.map(user => <FriendRequest hide={props.rerender} user={user} />)}
{messages == 0
- && <div
- style={{
- position: 'absolute',
- left: 0,
- right: 0,
- bottom: 0,
- top: 0,
- }}
- >
- <h1
- style={{
- position: 'absolute',
- top: '50%',
- left: '50%',
- whiteSpace: 'nowrap',
- transform: 'translate(-50%, -50%)',
- opacity: .7,
- }}
- >
+ && <div className='noMsgsWrapper posabs a0'>
+ <h1 className='posabs abscenter subtile'>
Geen meldingen
</h1>
</div>}
@@ -88,48 +50,24 @@ export function NotificationsArea(props: {
</Bubble>;
}
-var FriendRequestButtonStyle: CSSProperties = {
- borderRadius: 6,
- display: 'inline-block',
- marginLeft: 0,
- textAlign: 'center',
-};
-
function Acceptable(props: {
children?: ReactNode;
onAccept?: () => void;
onDeny?: () => void;
}) {
- return <Vierkant
- style={{
- borderRadius: 8,
- background: 'var(--background-alt)',
- margin: 0,
- padding: 12,
- width: '100%',
- marginBottom: 12,
- }}
- >
- <div style={{ position: 'relative' }}>
+ return <Vierkant className='acceptable bg-800 round-t pad-m fullwidth'>
+ <div className='posrel'>
{props.children}
- <div
- style={{
- display: 'grid',
- gridTemplateColumns: '1fr, 1fr',
- gridGap: 12,
- marginTop: 12,
- gridAutoFlow: 'column',
- }}
- >
+ <div className='sidebyside buttons'>
<IconLabelButton
+ className='accept'
onclick={props.onAccept}
- style={FriendRequestButtonStyle}
icon={<DoneIcon />}
text='Accepteren'
/>
<IconLabelButton
+ className='deny'
onclick={props.onDeny}
- style={FriendRequestButtonStyle}
icon={<CloseIcon />}
text='Verwijderen'
/>
@@ -171,14 +109,8 @@ function FriendRequest(props: {
>
<a href={'/user?id=' + props.user.id}>
<AccountAvatar size={48} id={props.user.id} />
- <div
- style={{
- display: 'inline-block',
- verticalAlign: 'top',
- marginLeft: 6,
- }}
- >
- <i style={{ display: 'block' }}>Vriendschapsverzoek</i>
+ <div className='userInfo dispinbl valigntop'>
+ <i className='dispbl'>Vriendschapsverzoek</i>
<b>{props.user.username}</b>
</div>
</a>
@@ -191,13 +123,8 @@ function GameInvite(props: {
}) {
return <Acceptable>
<a>
- <div
- style={{
- display: 'inline-block',
- verticalAlign: 'top',
- }}
- >
- <i style={{ display: 'block' }}>Partijuitnodiging</i>
+ <div className='userInfo dispinbl valigntop'>
+ <i className='dispbl'>Partijuitnodiging</i>
<p>
<b>
<a href={'/user?id=' + props.game.opponent?.id}>{props.game.opponent?.username}</a>
diff --git a/components/page.tsx b/components/page.tsx
index 506e2db..b8e8770 100644
--- a/components/page.tsx
+++ b/components/page.tsx
@@ -1,27 +1,15 @@
-import { Component, CSSProperties, ReactNode } from 'react';
+import { Component, ReactNode } from 'react';
-interface CenteredPageProps {
+export function CenteredPage(props: {
width?: number;
children?: ReactNode;
- style?: CSSProperties;
-}
-
-export function CenteredPage(props: CenteredPageProps) {
+ className?: string;
+}) {
return <div
className='CenteredPageOuter'
- style={{
- maxWidth: props.width,
- margin: '0 auto',
- }}
+ style={{ maxWidth: props.width }}
>
- <div
- className='CenteredPageInner'
- style={{
- margin: '0 6px',
- lineHeight: 0,
- ...props.style,
- }}
- >
+ <div className={'CenteredPageInner ' + props.className}>
{props.children}
</div>
</div>;
@@ -29,15 +17,7 @@ export function CenteredPage(props: CenteredPageProps) {
export class PageTitle extends Component {
render() {
- return <h1
- style={{
- color: 'var(--text-alt)',
- marginLeft: 6,
- marginTop: 32,
- marginBottom: 64,
- fontSize: 25,
- }}
- >
+ return <h1 className='pageTitle'>
{this.props.children}
</h1>;
}
diff --git a/components/recentGames.tsx b/components/recentGames.tsx
index 988126f..6683e20 100644
--- a/components/recentGames.tsx
+++ b/components/recentGames.tsx
@@ -1,18 +1,7 @@
import friendlyTime from 'friendly-time';
-import { CSSProperties } from 'react';
import { gameInfo } from '../api/api';
-var LeftAlignedTableColumn: CSSProperties = {
- textAlign: 'left',
- paddingLeft: 16,
-};
-
-var RightAlignedTableColumn: CSSProperties = {
- textAlign: 'right',
- paddingRight: 16,
-};
-
function GameOutcome(props: { game: gameInfo; }) {
var gameStatus = (() => {
return {
@@ -28,26 +17,24 @@ function GameOutcome(props: { game: gameInfo; }) {
},
}[props.game.status]();
})();
- var outcome = props.game.outcome;
- return <td
- style={{
- color: outcome == 'w'
- ? 'var(--disk-b-text)'
- : outcome == 'l'
- ? 'var(--disk-a-text)'
- : 'var(--text)',
- opacity: !['w', 'l'].includes(outcome) ? 0.75 : 1.0,
- }}
- >
- {gameStatus}
+ return <td>
+ <span
+ className={'outcome ' + {
+ 'w': 'win',
+ 'l': 'lose',
+ 'd': 'draw',
+ }[props.game.outcome]}
+ >
+ {gameStatus}
+ </span>
</td>;
}
export default function RecentGames(props: { games?: Array<gameInfo>; }) {
- return <div>
+ return <div className='recentGames'>
<h2>Recente partijen</h2>
{props.games?.length > 0
- ? <table width='100%' style={{ marginTop: '16px', textAlign: 'center' }}>
+ ? <table width='100%' className='recentGames center'>
<tbody>
<tr>
<th style={{ width: '50%' }}>Tegenstander</th>
@@ -57,19 +44,14 @@ export default function RecentGames(props: { games?: Array<gameInfo>; }) {
</tr>
{props.games?.map(game =>
<tr key={game.id}>
- <td style={LeftAlignedTableColumn}>
- <a
- href={'/user?id=' + game.opponent?.id}
- style={{
- fontWeight: 500,
- }}
- >
+ <td className='leftAlignedColumn'>
+ <a href={'/user?id=' + game.opponent?.id}>
{game.opponent?.username}
</a>
</td>
<GameOutcome game={game} />
<td>{Math.max(0, game.moves.length - 1)}</td>
- <td style={RightAlignedTableColumn}>
+ <td className='rightAlignedColumn'>
{(() => {
var timeCreated = new Date(game.created);
return friendlyTime(timeCreated);
@@ -79,13 +61,7 @@ export default function RecentGames(props: { games?: Array<gameInfo>; }) {
)}
</tbody>
</table>
- : <h1
- style={{
- textAlign: 'center',
- opacity: .6,
- margin: '32px 64px',
- }}
- >
+ : <h1 className='nogames center subtile'>
Deze gebruiker heeft nog geen partijen gespeeld
</h1>}
</div>;
diff --git a/components/toast.tsx b/components/toast.tsx
index 97e17e6..3a72ae1 100644
--- a/components/toast.tsx
+++ b/components/toast.tsx
@@ -1,26 +1,14 @@
-import { createContext, CSSProperties, ReactNode, useState } from 'react';
+import { createContext, ReactNode, useState } from 'react';
import CloseIcon from '@material-ui/icons/Close';
function ToastArea(props: {
- style?: CSSProperties;
children?: ReactNode;
rerender?: boolean;
}) {
return <div
id='ToastArea'
- style={{
- position: 'fixed',
- whiteSpace: 'nowrap',
- bottom: 12,
- left: '50%',
- transform: 'translateX(-50%)',
- zIndex: 1,
- maxWidth: 600,
- width: 'calc(100% - 48px - 48px)',
- margin: '0 24px',
- ...props.style,
- }}
+ className='posfix abscenterh'
>
{props.children}
</div>;
@@ -32,71 +20,30 @@ function Toast(props: {
icon?: ReactNode;
children?: ReactNode;
type?: 'normal' | 'confirmation' | 'error';
- style?: CSSProperties;
}) {
var [visible, setVisibility] = useState(true);
setTimeout(() => setVisibility(false), 10e3);
- return visible && <div
- style={{
- padding: 0,
- marginBottom: 12,
- borderRadius: 6,
- boxShadow: '0 8px 12px -4px #00000033',
- color: props.type === 'confirmation' ? 'var(--background)' : 'var(--text)',
- backgroundColor: props.type === 'normal'
- ? 'var(--background)'
- : props.type === 'confirmation'
- ? 'var(--disk-b)'
- : props.type === 'error'
- ? 'var(--disk-a)'
- : 'var(--background)',
- ...props.style,
- }}
- >
+ return visible && <div className={'round-t drop-1 toast ' + props.type}>
{props.children
|| <div
- style={{
- lineHeight: 0,
- padding: 12,
- minHeight: props.description ? 36 : 24,
- position: 'relative',
- }}
+ className={'inner pad-m posrel '
+ + (props.description ? 'hasDescription' : '') + ' '
+ + (props.icon ? 'hasIcon' : '')}
>
- <div
- style={{
- position: 'absolute',
- left: 12,
- top: '50%',
- transform: 'translateY(-50%)',
- }}
- >
+ <div className='icon posabs abscenterv'>
{props.icon}
</div>
- <div
- style={{
- userSelect: 'none',
- position: 'absolute',
- left: props.icon ? 48 : 12,
- top: '50%',
- transform: 'translateY(-50%)',
- }}
- >
- <h2 style={{ fontSize: 16 }}>{props.text}</h2>
+ <div className='content nosel posabs abscenterv'>
+ <h2>{props.text}</h2>
<p>{props.description}</p>
</div>
<div
- style={{
- cursor: 'pointer',
- position: 'absolute',
- right: 12,
- top: '50%',
- transform: 'translateY(-50%)',
- }}
+ className='closeIcon posabs abscenterv'
onClick={() => setVisibility(false)}
>
- <CloseIcon style={{ fontSize: 24 }} />
+ <CloseIcon />
</div>
</div>}
</div>;
diff --git a/components/ui.tsx b/components/ui.tsx
index c3f950b..7474240 100644
--- a/components/ui.tsx
+++ b/components/ui.tsx
@@ -1,4 +1,4 @@
-import { Component, CSSProperties, ReactNode, useEffect, useState } from 'react';
+import { ReactNode, useEffect, useState } from 'react';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
@@ -9,33 +9,14 @@ export function Vierkant(props: {
href?: string;
width?: string;
height?: string;
- style?: CSSProperties;
children?: ReactNode;
className?: string;
id?: string;
- fullwidth?: boolean;
onclick?: () => void;
}) {
return <a
- style={{
- padding: 24,
- backgroundColor: 'var(--background)',
- borderRadius: 8,
- color: 'var(--text)',
- margin: 6, // geen margin collapse = 12px marge
- display: 'inline-block',
- position: 'relative',
- boxSizing: 'border-box',
- width: props.width
- ? props.width
- : props.fullwidth
- ? 'calc(100% - 12px)'
- : undefined,
- height: props.height ? props.height : undefined,
- ...props.style,
- }}
href={props.href}
- className={props.className}
+ className={['round-l', 'vierkant', props.className].join(' ')}
id={props.id}
onClick={props.onclick}
>
@@ -46,35 +27,19 @@ export function Vierkant(props: {
export function Button(props: {
text?: string;
children?: ReactNode;
- style?: CSSProperties;
href?: string;
- onclick?: (() => void);
+ className?: string;
+ onclick?: () => void;
id?: string;
}) {
return <a
onClick={props.onclick}
href={props.href}
id={props.id}
- style={{
- padding: props.text ? 8 : 16,
- textAlign: props.text ? 'center' : 'left',
- borderRadius: 8,
- backgroundColor: 'var(--disk-a)',
- cursor: 'pointer',
- position: 'relative',
- textDecoration: 'none',
- display: 'block',
- userSelect: 'none',
- ...props.style,
- }}
+ className={'button pad-s round-t ' + props.className}
>
{props.text
- ? <span
- style={{
- fontWeight: 600,
- userSelect: 'none',
- }}
- >
+ ? <span>
{props.text}
</span>
: undefined}
@@ -86,33 +51,18 @@ export function IconLabelButton(props: {
text: string;
icon: ReactNode;
onclick?: () => void;
- style?: CSSProperties;
href?: string;
+ className?: string;
}) {
return <Button
onclick={props.onclick}
href={props.href}
- style={{
- display: 'inline-block',
- verticalAlign: 'top',
- padding: 8,
- float: 'right',
- marginLeft: 12,
- ...props.style,
- }}
+ className={'iconlabel dispinbl valigntop floatr' + ' ' + props.className}
>
- {props.icon}
- <span
- style={{
- display: 'inline-block',
- verticalAlign: 'top',
- fontWeight: 500,
- marginLeft: 8,
- marginTop: 3,
- marginBottom: 3,
- marginRight: 3,
- }}
- >
+ <div className='dispinbl icon'>
+ {props.icon}
+ </div>
+ <span className='dispinbl valigntop label'>
{props.text}
</span>
</Button>;
@@ -120,7 +70,6 @@ export function IconLabelButton(props: {
export function Input(props: {
label?: string;
- style?: CSSProperties;
type?: string;
id?: string;
min?: number;
@@ -129,6 +78,7 @@ export function Input(props: {
dark?: boolean;
autocomplete?: string;
autofocus?: boolean;
+ className?: string;
}) {
return <input
id={props.id}
@@ -138,65 +88,26 @@ export function Input(props: {
placeholder={props.label}
spellCheck={false}
defaultValue={props.value ? String(props.value) : ''}
- className={props.dark ? 'dark' : 'light'}
+ className={'input' + ' ' + (props.dark ? 'dark' : 'light') + ' ' + props.className}
autoComplete={props.autocomplete}
autoFocus={props.autofocus}
- style={{
- padding: 12,
- border: 0,
- width: 'calc(100% - 24px)',
- fontSize: 14,
- backgroundColor: 'var(--page-background)',
- color: 'var(--text-alt)',
- borderRadius: 8,
- fontFamily: 'Inter',
- ...props.style,
- }}
/>;
}
export function SearchBar(props: { label?: string; }) {
- return <div
- style={{
- marginTop: 24,
- borderRadius: 8,
- overflow: 'hidden',
- width: '100%',
- }}
- >
+ return <div className='searchBar round-t fullwidth'>
<Input
label={props.label}
- style={{
- width: 'calc(100% - 24px - 41px)',
- borderTopRightRadius: 0,
- borderBottomRightRadius: 0,
- }}
+ className='pad-m bg-700'
/>
- <div
- style={{
- width: 41,
- height: 41,
- backgroundColor: 'var(--disk-a)',
- display: 'inline-block',
- verticalAlign: 'top',
- position: 'relative',
- }}
- >
- <SearchIcon
- style={{
- position: 'absolute',
- top: '50%',
- left: '50%',
- transform: 'translate(-50%, -50%)',
- }}
- />
- </div>
+ <Button className='dispinbl valigntop'>
+ <SearchIcon className='icon' />
+ </Button>
</div>;
}
export function CheckBox(props: {
state?: boolean;
- style?: CSSProperties;
id?: string;
onclick?: (state: boolean) => void;
}) {
@@ -217,12 +128,7 @@ export function CheckBox(props: {
return <div
onClick={toggle}
id={props.id}
- className={on ? 'on' : 'off'}
- style={{
- ...props.style,
- display: 'inline-block',
- cursor: 'pointer',
- }}
+ className={'checkbox dispinbl ' + (on ? 'on' : 'off')}
>
{on
? <CheckBoxIcon />
@@ -230,110 +136,41 @@ export function CheckBox(props: {
</div>;
}
-export class ColorPicker extends Component<{
- style?: CSSProperties;
-}> {
- state: {
- color: string;
- dark: boolean;
- } = {
- color: '#012345',
- dark: true,
- };
+export function ColorPicker() {
+ var [dark, setDark] = useState(false);
+ var [color, setColor] = useState('#012345');
- render() {
- return <Button
- style={{
- display: 'inline-block',
- verticalAlign: 'top',
- padding: 6,
- float: 'right',
- marginLeft: 12,
- color: this.state.dark ? 'var(--text)' : 'var(--text-alt)',
- borderColor: this.state.dark ? 'var(--text)' : 'var(--text-alt)',
- borderWidth: 2,
- borderStyle: 'solid',
- backgroundColor: this.state.color,
- ...this.props.style,
- }}
- >
- <div>
- <EditOutlinedIcon />
- <div
- style={{
- width: 150,
- height: 24,
- display: 'inline-block',
- textAlign: 'center',
- verticalAlign: 'top',
- position: 'relative',
- }}
- >
- <span
- style={{
- position: 'absolute',
- top: '50%',
- left: '50%',
- transform: 'translate(-50%, -50%)',
- fontWeight: 600,
- fontFeatureSettings: '"tnum", "ss01"',
- }}
- >
- {this.state.color}
- </span>
- </div>
+ return <Button className='colorpicker dispinbl valigntop pad-s floatr'>
+ <div>
+ <EditOutlinedIcon />
+ <div className='labelwrapper valigntop dispinbl center posrel'>
+ <span className='label posabs'>
+ {color}
+ </span>
</div>
- </Button>;
- }
+ </div>
+ </Button>;
}
-export function Tuitje(props: {
- style?: CSSProperties;
- rotation?: number;
-}) {
+export function Tuitje() {
return <svg
width='36'
height='12'
viewBox='0 0 36 12'
fill='none'
xmlns='http://www.w3.org/2000/svg'
- style={{
- ...props.style,
- }}
+ className='tuitje posabs'
>
- <path
- d='M18 12C24 12 27 0 36 0L0 0C9 0 12 12 18 12Z'
- fill={props.style?.background as string || 'var(--background)'}
- />
+ <path d='M18 12C24 12 27 0 36 0L0 0C9 0 12 12 18 12Z' />
</svg>;
}
export function Bubble(props: {
children?: ReactNode;
- style?: CSSProperties;
- tuitjeStyle?: CSSProperties;
+ className?: string;
}) {
- return <Vierkant
- style={{
- position: 'absolute',
- textAlign: 'center',
- margin: 0,
- overflow: 'visible',
- left: '50%',
- top: -24,
- transform: 'translateY(-100%) translateX(-50%)',
- boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
- ...props.style,
- }}
- >
+ return <Vierkant className={'bubble posabs center drop-2 ' + props.className}>
{props.children}
- <Tuitje
- style={{
- position: 'absolute',
- bottom: -12,
- transform: 'translate(-50%, 0%) rotate(0deg)',
- ...props.tuitjeStyle,
- }}
- />
+ <Tuitje />
</Vierkant>;
}
diff --git a/components/voerBord.tsx b/components/voerBord.tsx
index 93e350c..ffb291b 100644
--- a/components/voerBord.tsx
+++ b/components/voerBord.tsx
@@ -1,62 +1,22 @@
-function Disc() {
- return <div
- className='disk'
- style={{
- position: 'absolute',
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- borderRadius: 999999,
- margin: 3,
- }}
- />;
-}
-
export function VoerBord(props: {
width: number;
height: number;
onMove: (move: number) => void;
active: boolean;
}) {
- return <table
- className='voerBord'
- style={{
- borderSpacing: 8,
- width: '100%',
- }}
- >
+ return <table className={'voerBord fullwidth ' + (props.active ? 'active' : '')}>
<tbody>
{[...Array(props.height).keys()].map((row) => (
<tr key={`r-${row}`}>
{[...Array(props.width).keys()].map((column) => (
<td
- style={{
- position: 'relative',
- width: '100%',
- padding: 0,
- }}
+ className='posrel outer cell fullwidth'
key={`c-${row}x${column}`}
>
+ <div className='dispbl square' />
+ <div className='disk posabs a0' />
<div
- style={{
- display: 'block',
- marginTop: '100%',
- }}
- />
- <Disc />
- <div
- style={{
- position: 'absolute',
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- borderRadius: 6,
- border: '2px solid var(--background-alt)',
- opacity: .5,
- cursor: props.active ? 'pointer' : 'default',
- }}
+ className='posabs a0 round-t inner cell'
id={`pos-${(props.height - row - 1) * props.width + column}`}
onClick={event => {
props.onMove(Number((event.target as HTMLElement).id.split('-')[1]));