Merge pull request #32 from jhalitaksoy/fix/ui-tweaks

Fix/UI tweaks
This commit is contained in:
Halit Aksoy 2022-09-04 11:09:27 +03:00 committed by GitHub
commit 77084c3aff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 179 additions and 54 deletions

View File

@ -13,17 +13,19 @@
"author": "",
"license": "ISC",
"dependencies": {
"@szhsin/react-menu": "^3.0.2",
"@szhsin/react-menu": "^3.1.2",
"@types/": "szhsin/react-menu",
"@types/eventemitter2": "^4.1.0",
"@types/styled-jsx": "^3.4.4",
"@types/uuid": "^8.3.4",
"eventemitter2": "^6.4.7",
"mancala.js": "^0.0.2-beta.3",
"notyf": "^3.10.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "6",
"styled-jsx": "^5.0.2",
"sweetalert": "^2.1.2",
"uuid": "^8.3.2"
},
"devDependencies": {

View File

@ -12,9 +12,11 @@ import Home from './routes/Home';
import { initContext } from './context/context';
import { RTMTWS } from './rtmt/rtmt_websocket';
import { getColorByBrightness } from './util/ColorUtil';
import { ConnectionState } from './models/ConnectionState';
import { Theme } from './theme/Theme';
import LobyPage from './routes/LobyPage';
import swal from 'sweetalert';
import { ConnectionState } from './rtmt/rtmt';
const context = initContext();
const MancalaApp: FunctionComponent = () => {
@ -40,9 +42,15 @@ const MancalaApp: FunctionComponent = () => {
setTheme(theme)
}
const connectToServer = async () => {
const userKey = await context.userKeyStore.getUserKey();
setUserKey(userKey);
(context.rtmt as RTMTWS).initWebSocket(userKey);
try {
const userKey = await context.userKeyStore.getUserKey();
setUserKey(userKey);
(context.rtmt as RTMTWS).initWebSocket(userKey);
} catch (error) {
//TODO: check if it is network error!
swal(context.texts.Error + "!", context.texts.ErrorWhenRetrievingInformation, "error");
console.error(error);
}
};
React.useEffect(() => {
@ -74,7 +82,6 @@ const MancalaApp: FunctionComponent = () => {
context.themeManager.theme.textColor,
context.themeManager.theme.textLightColor
);
if (!userKey) return <></>;
return (
<>
<BrowserRouter>
@ -82,7 +89,7 @@ const MancalaApp: FunctionComponent = () => {
<Route index element={<Home context={context} theme={theme} userKey={userKey} />} />
<Route path="/" >
<Route path="game" >
<Route path=":gameId" element={<GamePage context={context} theme={theme} userKey={userKey} connectionState={connectionState} />} ></Route>
<Route path=":gameId" element={<GamePage context={context} theme={theme} userKey={userKey} />} ></Route>
</Route>
<Route path="loby" element={<LobyPage context={context} theme={theme} userKey={userKey} />}>
</Route>
@ -90,7 +97,7 @@ const MancalaApp: FunctionComponent = () => {
</Routes>
</BrowserRouter>
<FloatingPanel context={context} color={floatingPanelColor} visible={showConnectionState}>
<span style={{ color: textColorOnBoard }}>{connectionStateText()}</span>
<span style={{ color: textColorOnBoard, transition: 'color 0.5s' }}>{connectionStateText()}</span>
</FloatingPanel>
</>
);

View File

@ -13,6 +13,7 @@ import { Context } from "../context/context";
import BoardViewModelFactory from "../factory/BoardViewModelFactory";
import { PitViewModelFactory } from "../factory/PitViewModelFactory";
import { Game } from "../models/Game";
import { Theme } from "../theme/Theme";
import { getColorByBrightness } from "../util/ColorUtil";
import BoardViewModel from "../viewmodel/BoardViewModel";
@ -35,6 +36,7 @@ export default class PitAnimator {
) {
this.context = context;
this.onBoardViewModelUpdate = onBoardViewModelUpdate;
this.context.themeManager.on("themechange", this.onThemeChange.bind(this));
}
get mancalaGame(): MancalaGame | undefined {
@ -190,6 +192,11 @@ export default class PitAnimator {
});
}
private onThemeChange(theme: Theme){
if(!this.game) return;
this.onBoardViewModelUpdate?.(this.getBoardViewModelFromGame(this.game));
}
public resetAnimationState() {
this.animationIndex = -1;
this.currentHistoryItem = undefined;
@ -204,6 +211,7 @@ export default class PitAnimator {
}
public dispose() {
this.context.themeManager.off("themechange", this.onThemeChange.bind(this));
this.resetAnimationState();
this.clearCurrentInterval();
}

View File

@ -18,7 +18,7 @@ const Button: FunctionComponent<{
<button
onClick={onClick}
style={{
background: color,
backgroundColor: color,
color: textColor,
}}
>
@ -28,6 +28,9 @@ const Button: FunctionComponent<{
padding: 10px;
border: none;
border-radius: 30px;
transition: color 0.5s;
transition: background-color 0.5s;
white-space: nowrap;
}
`}</style>
{text}

View File

@ -11,6 +11,8 @@ const CircularPanel: FunctionComponent<{
div {
padding: 10px 20px;
border-radius: 30px;
transition: background-color 0.5s;
white-space: nowrap;
}
`}
</style>

View File

@ -99,7 +99,7 @@ const InfoPanel: FunctionComponent<{
if (text) {
return (
<CircularPanel style={style} color={containerColor}>
<h4 style={{ margin: "0", color: textColor }}>
<h4 style={{ margin: "0", color: textColor, transition: 'color 0.5s' }}>
{text}
</h4>
</CircularPanel>

View File

@ -14,6 +14,7 @@ const PageContainer: FunctionComponent<{ theme: Theme }> = (props) => {
align-items: center;
flex: 1;
min-height: 400px;
transition: background-color 0.5s;
}
`}</style>
{props.children}

View File

@ -16,17 +16,13 @@ const UserStatus: FunctionComponent<{
}> = ({ context, user, layoutMode, visible, style }) => {
if (visible === false) return <></>;
const textColorOnBoard = getColorByBrightness(
context.themeManager.theme.boardColor,
context.themeManager.theme.background,
context.themeManager.theme.textColor,
context.themeManager.theme.textLightColor
);
return (
<div style={style} className={layoutMode === "right" ? "flex-rtl" : "flex-ltr"}>
<span style={{color: textColorOnBoard}} className="material-symbols-outlined icon" >
face_6
</span>
<Space width='5px' />
<span style={{color: textColorOnBoard}} className='text'>{user.isAnonymous ? context.texts.Anonymous : user.name}</span>
<span style={{color: textColorOnBoard, transition: 'color 0.5s'}} className='text'>{user.isAnonymous ? context.texts.Anonymous : user.name}</span>
<Space width='5px' />
<div className={"circle " + (user.isOnline ? "online" : "offline")}></div>
<style jsx>{`
@ -43,6 +39,8 @@ const UserStatus: FunctionComponent<{
min-height: 15px;
border-radius: 15px;
border: 2px solid ${context.themeManager.theme.boardColor};
transition: background-color 0.5s;
transition: color 0.5s;
}
.flex-rtl {
display: flex;

View File

@ -29,7 +29,7 @@ const StoreView: FunctionComponent<{
gridRow: gridRow
}}>
{stones}
<span className="store-stone-count-text" style={{ color: textColor }}>
<span className="store-stone-count-text" style={{ color: textColor, transition: 'color 0.5s' }}>
{stones.length}
</span>
<style jsx>{`
@ -42,6 +42,7 @@ const StoreView: FunctionComponent<{
align-content: center;
flex-wrap: wrap;
position: relative;
transition: background-color 0.5s;
}
.store-stone-count-text {
position: absolute;

View File

@ -13,6 +13,7 @@ const HeaderBar: FunctionComponent<{ color?: string }> = ({children, color }) =>
align-items: center;
justify-content: space-between;
align-self: stretch;
transition: background-color 0.5s;
}
`}</style>
</div>)

View File

@ -4,7 +4,7 @@ import { Context } from '../../context/context';
const HeaderbarTitle: FunctionComponent<{ title: string, color: string }> = ({ title, color }) => {
return (
<h1 style={{ color: color }} className="header-bar-title">
<h1 style={{ color: color, transition: 'color 0.5s' }} className="header-bar-title">
{title}
<style jsx>{`
.header-bar-title {

View File

@ -8,22 +8,27 @@ import "@szhsin/react-menu/dist/transitions/slide.css"
const ThemeSwitchMenu: FunctionComponent<{ context: Context, textColor: string }> = (props) => {
const { context, textColor } = props;
const menuButton = <span
style={{ color: textColor }}
style={{ color: textColor, cursor: 'pointer', userSelect: 'none' }}
className="material-symbols-outlined">
light_mode
</span>;
const menuItems = context.themeManager.themes.map((theme, index) => {
const themeBackground = context.themeManager.theme.background;
return (
<MenuItem
key={index}
style={{ color: textColor }}
//@ts-ignore
onMouseOver={(event) => (event.target.style.background = themeBackground)}
onMouseOver={(event) => {
const htmlElement: HTMLElement = event.target as HTMLElement;
if (htmlElement.localName === "li") htmlElement.style.background = "transparent";
}}
//@ts-ignore
onMouseOut={(event) => (event.target.style.background = "transparent")}
onMouseOut={(event) => {
const htmlElement: HTMLElement = event.target as HTMLElement;
if (htmlElement.localName === "li") htmlElement.style.background = "transparent";
}}
onClick={() => (context.themeManager.theme = theme)}>
<div style={{ background: theme.boardColor }} className="theme-color-circle" />
<div style={{ background: theme.themePreviewColor }} className="theme-color-circle" />
{theme.name}
<style jsx>{`
.theme-color-circle {

View File

@ -6,7 +6,7 @@ export type Texts = {
YourTurn: string,
OpponentTurn: string,
GameEnded: string,
GameCrashed: string,
InternalErrorOccurred: string,
YouWon: string,
Won: string,
YouLost: string,
@ -21,12 +21,20 @@ export type Texts = {
YouLeftTheGame: string,
UserLeftTheGame: string,
SearchingOpponent: string,
PleaseWait : string,
GameDraw : string,
PleaseWait: string,
GameDraw: string,
Anonymous: string,
GameNotFound: string,
Loading: string,
Playing: string,
Error: string,
ErrorWhenRetrievingInformation: string,
UCanOnlyPlayYourOwnPits: string,
UMustWaitUntilCurrentMoveComplete: string,
UCanNotPlayEmptyPit: string,
AreYouSureToLeaveGame: string,
Yes: string,
Cancel: string,
}
export const EnUs: Texts = {
@ -36,14 +44,14 @@ export const EnUs: Texts = {
YourTurn: "Your Turn",
OpponentTurn: "Opponent Turn",
GameEnded: "Game Ended",
GameCrashed: "Game Crashed",
InternalErrorOccurred: "An internal error has occurred",
YouWon: "You Won",
Won: "Won",
YouLost: "You Lost",
Connecting: "Connecting",
Connected: "Connected",
CannotConnect: "Can't Connect",
ConnectionLost: "Connection Lost",
ConnectionLost: "Network Connection Lost",
ConnectingAgain: "Connecting Again",
ServerError: "Server Error",
SearchingOpponet: "Searching Opponet",
@ -51,12 +59,20 @@ export const EnUs: Texts = {
YouLeftTheGame: "You Left The Game",
UserLeftTheGame: "Left The Game",
SearchingOpponent: "Searching Opponent",
PleaseWait : "Please Wait",
GameDraw : "Draw",
PleaseWait: "Please Wait",
GameDraw: "Draw",
Anonymous: "Anonymous",
GameNotFound: "Game Not Found",
Loading: "Loading",
Playing: "Playing",
Error: "Error",
ErrorWhenRetrievingInformation: "An error occured when retrieving information!",
UCanOnlyPlayYourOwnPits: "You can only play your own pits",
UMustWaitUntilCurrentMoveComplete: "You must wait until the current move is complete",
UCanNotPlayEmptyPit: "You can not play empty pit",
AreYouSureToLeaveGame: "Are you sure to leave game?",
Yes: "Yes",
Cancel: "Cancel",
}
export const TrTr: Texts = {
@ -66,14 +82,14 @@ export const TrTr: Texts = {
YourTurn: "Sıra Sende",
OpponentTurn: "Sıra Rakipte",
GameEnded: "Oyun Bitti",
GameCrashed: "Oyunda Hata Oluştu",
InternalErrorOccurred: "Dahili bir hata oluştu",
YouWon: "Kazandın",
Won: "Kazandı",
YouLost: "Kaybettin",
Connecting: "Bağlanılıyor",
Connected: "Bağlandı",
CannotConnect: "Bağlanılamadı",
ConnectionLost: "Bağlantı Koptu",
ConnectionLost: "Bağlantısı Koptu",
ConnectingAgain: "Tekrar Bağlanılıyor",
ServerError: "Sunucu Hatası",
SearchingOpponet: "Rakip Aranıyor",
@ -82,9 +98,17 @@ export const TrTr: Texts = {
UserLeftTheGame: "Oyundan Ayrıldı",
SearchingOpponent: "Rakip Aranıyor",
PleaseWait: "Lütfen Bekleyin",
GameDraw : "Berabere",
GameDraw: "Berabere",
Anonymous: "Anonim",
GameNotFound: "Oyun Bulunamadı",
Loading: "Yükleniyor",
Playing: "Oynuyor",
Error: "Hata",
ErrorWhenRetrievingInformation: "Bilgiler toplanırken bir hata oluştu!",
UCanOnlyPlayYourOwnPits: "Sadece sana ait olan kuyular ile oynayabilirsin",
UMustWaitUntilCurrentMoveComplete: "Devam eden haraketin bitmesini beklemelisin",
UCanNotPlayEmptyPit: "Boş kuyu ile oynayamazsın",
AreYouSureToLeaveGame: "Oyundan ayrılmak istediğine emin misin?",
Yes: "Evet",
Cancel: "İptal"
}

View File

@ -1 +0,0 @@
export type ConnectionState = "connecting" | "error" | "connected" | "reconnecting";

View File

@ -20,7 +20,6 @@ import UserStatus from '../components/UserStatus';
import { channel_on_game_update, channel_on_game_crashed, channel_on_game_user_leave, channel_on_user_connection_change, channel_leave_game, channel_game_move, channel_listen_game_events, channel_unlisten_game_events } from '../const/channel_names';
import { Context } from '../context/context';
import useWindowDimensions from '../hooks/useWindowDimensions';
import { ConnectionState } from '../models/ConnectionState';
import { GameMove } from '../models/GameMove';
import { LoadingState } from '../models/LoadingState';
import { Theme } from '../theme/Theme';
@ -28,13 +27,15 @@ import { getColorByBrightness } from '../util/ColorUtil';
import BoardViewModel from '../viewmodel/BoardViewModel';
import Center from '../components/Center';
import { Game, GameUsersConnectionInfo } from '../models/Game';
import notyf from '../util/Notyf';
import swal from 'sweetalert';
import Util from '../util/Util';
const GamePage: FunctionComponent<{
context: Context,
userKey?: string,
theme: Theme,
connectionState: ConnectionState
}> = ({ context, userKey, theme, connectionState }) => {
}> = ({ context, userKey, theme }) => {
let params = useParams<{ gameId: string }>();
const [game, setGame] = useState<Game | undefined>(undefined);
@ -89,6 +90,7 @@ const GamePage: FunctionComponent<{
}
const onGameCrashed = (message: any) => {
const newCrashMessage = message as string;
notyf.error(context.texts.InternalErrorOccurred);
console.error("on_game_crash");
console.error(newCrashMessage);
}
@ -136,10 +138,22 @@ const GamePage: FunctionComponent<{
const checkHasAnOngoingAction = () => hasOngoingAction;
const onLeaveGameClick = () => {
context.rtmt.sendMessage(channel_leave_game, {});
if(Util.checkConnectionAndMaybeAlert(context)) return;
swal({
title: context.texts.AreYouSureToLeaveGame,
icon: "warning",
buttons: [context.texts.Yes, context.texts.Cancel],
dangerMode: true,
})
.then((cancel) => {
if (!cancel) {
context.rtmt.sendMessage(channel_leave_game, {});
}
});
};
const onNewGameClick = () => {
if(Util.checkConnectionAndMaybeAlert(context)) return;
navigate("/loby")
};
@ -147,21 +161,27 @@ const GamePage: FunctionComponent<{
if (!game || isSpectator || !userKey) {
return;
}
if (game.mancalaGame.getPlayerIdByIndex(index) !== userKey) {
notyf.error(context.texts.UCanOnlyPlayYourOwnPits);
return;
}
const pitIndexForUser = index % (game.mancalaGame.board.totalPitCount() / 2);
if (game.mancalaGame.getPlayerIdByIndex(index) !== userKey ||
!game.mancalaGame.canPlayerMove(userKey, pitIndexForUser)) {
if (!game.mancalaGame.canPlayerMove(userKey, pitIndexForUser)) {
notyf.error(context.texts.OpponentTurn);
return;
}
if (checkHasAnOngoingAction()) {
notyf.error(context.texts.UMustWaitUntilCurrentMoveComplete);
return;
}
setHasOngoingAction(true);
if(Util.checkConnectionAndMaybeAlert(context)) return;
if (!boardViewModel) return;
//TODO: this check should be in mancala.js
if (pit.stoneCount === 0) {
//TODO : warn user
notyf.error(context.texts.UCanNotPlayEmptyPit);
return;
}
setHasOngoingAction(true);
boardViewModel.pits[getBoardIndex(pitIndexForUser)].pitColor =
context.themeManager.theme.pitSelectedColor;
updateBoardViewModel(boardViewModel);
@ -210,13 +230,13 @@ const GamePage: FunctionComponent<{
const bottomLocatedUser = {
id: bottomLocatedUserId,
name: "Anonymous",
isOnline: isSpectator ? isUserOnline(bottomLocatedUserId) : connectionState === "connected",
isOnline: isSpectator ? isUserOnline(bottomLocatedUserId) : context.rtmt.connectionState === "connected",
isAnonymous: true
};
const currentUser = isSpectator ? {
id: "2",
name: "Anonymous",
isOnline: connectionState === "connected",
isOnline: context.rtmt.connectionState === "connected",
isAnonymous: true
} : bottomLocatedUser;
const leftPlayer = userKeyWhoLeave ? (userKeyWhoLeave === topLocatedUser.id ? topLocatedUser : bottomLocatedUser) : undefined;
@ -263,7 +283,7 @@ const GamePage: FunctionComponent<{
function renderMobileBoardToolbar() {
return <BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
{buildInfoPanel()}
{buildInfoPanel({ visible: isMobile })}
</BoardToolbar>;
}
@ -273,7 +293,7 @@ const GamePage: FunctionComponent<{
marginBottom: "0.5rem", marginLeft: "6%", maxWidth: isMobile ? "40vw" : "30vw",
width: isMobile ? "40vw" : "30vw"
}} context={context} layoutMode="left" user={topLocatedUser} visible={showBoardView || false} />
{buildInfoPanel()}
{buildInfoPanel({ visible: !isMobile })}
<UserStatus style={{
marginBottom: "0.5rem", marginRight: "6%", maxWidth: isMobile ? "40vw" : "30vw",
width: isMobile ? "40vw" : "30vw"
@ -281,7 +301,7 @@ const GamePage: FunctionComponent<{
</BoardToolbar>;
}
function buildInfoPanel() {
function buildInfoPanel(params: { visible: boolean }) {
return (
<InfoPanel
style={{ marginTop: "0.5rem", marginBottom: "0.5rem" }}
@ -291,7 +311,7 @@ const GamePage: FunctionComponent<{
whitePlayer={topLocatedUser}
blackPlayer={bottomLocatedUser}
leftPlayer={leftPlayer}
visible={!isMobile}
visible={params.visible}
isSpectator={isSpectator} />
);
}

View File

@ -11,6 +11,7 @@ import ThemeSwitchMenu from "../components/headerbar/ThemeSwitchMenu";
import Button from "../components/Button";
import { Context } from "../context/context";
import { Link, useNavigate } from "react-router-dom";
import Util from "../util/Util";
const Home: FunctionComponent<{
context: Context,
@ -21,6 +22,7 @@ const Home: FunctionComponent<{
const navigate = useNavigate();
const onNewGameClick = () => {
if(Util.checkConnectionAndMaybeAlert(context)) return;
navigate("/loby")
};

View File

@ -3,10 +3,12 @@ import EventEmitter2, { Listener } from "eventemitter2"
export type Bytes = Uint8Array
export type OnMessage = (message : Object) => any
export type ConnectionState = "none" | "connecting" | "error" | "connected" | "closed" | "reconnecting";
export type RtmtEventTypes = "open" | "close" | "connected" | "error" | "disconnected" | "message";
export interface RTMT extends EventEmitter2 {
get connectionState() : ConnectionState;
sendMessage: (channel: string, message: Object) => void;
addMessageListener(channel: string, callback: (message: any) => void);
removeMessageListener(channel: string, callback: (message: any) => void);

View File

@ -2,7 +2,7 @@ import { decode, encode } from "./encode_decode_message";
import { channel_ping, channel_pong } from "../const/channel_names";
import { server } from "../const/config";
import EventEmitter2, { Listener } from "eventemitter2";
import { Bytes, RTMT, RtmtEventTypes } from "./rtmt";
import { Bytes, ConnectionState, RTMT, RtmtEventTypes } from "./rtmt";
const PING_INTERVAL = 15000, PING_INTERVAL_BUFFER_TIME = 1000;
const MESSAGE_CHANNEL_PREFIX = "message_channel";
@ -10,24 +10,32 @@ const MESSAGE_CHANNEL_PREFIX = "message_channel";
export class RTMTWS extends EventEmitter2 implements RTMT {
private webSocket: WebSocket;
private pingTimeout?: number = undefined;
private _connectionState: ConnectionState = "none";
constructor() {
super();
}
get connectionState(): ConnectionState {
return this._connectionState;
}
public initWebSocket(userKey: string) {
this._connectionState = this._connectionState !== "none" ? "reconnecting" : "connecting";
const url = server.wsServerAdress + "?userKey=" + userKey;
const webSocket = new WebSocket(url);
webSocket.onopen = () => {
console.info("(RTMT) WebSocket has opened");
this.webSocket = webSocket;
this.heartbeat();
this._connectionState = "connected";
this.emit("open");
};
webSocket.onclose = () => {
console.info("(RTMT) WebSocket has closed");
//this.WebSocket = undefined
clearTimeout(this.pingTimeout);
this._connectionState = "closed";
this.emit("close");
};
webSocket.onmessage = (event: MessageEvent) => {
@ -36,6 +44,7 @@ export class RTMTWS extends EventEmitter2 implements RTMT {
};
webSocket.onerror = (error) => {
console.error(error);
this._connectionState = "error";
this.emit("error", error);
}
}

View File

@ -13,6 +13,7 @@ const colorSpecial = "#337a44";
const darkTheme: Theme = {
id: "2",
name: "Dark Theme",
themePreviewColor: colors.primary,
background: colors.primary,
appBarBgColor: colors.secondary,
textColor: colors.primary,

View File

@ -3,6 +3,7 @@ import { Theme } from "./Theme";
const greyTheme: Theme = {
id: "1",
name: "Grey Theme",
themePreviewColor: "#4D606E",
background: "#EEEEEE",
appBarBgColor: "#e4e4e4",
textColor: "#4D606E",

View File

@ -5,6 +5,7 @@ const colorSpecial = "#8B8B8B";
const lightTheme: Theme = {
id: "1",
name: "Light Theme",
themePreviewColor: "#9B9B9B",
background: "#BBBBBB",
appBarBgColor: "#7B7B7B",
textColor: "#5B5B5B",

View File

@ -1,6 +1,7 @@
export type Theme = {
id: string;
name: string;
themePreviewColor: string; // for theme switch menu
textColor: string;
textLightColor: string;
background: string;

4
src/util/Notyf.ts Normal file
View File

@ -0,0 +1,4 @@
import { Notyf } from 'notyf';
import 'notyf/notyf.min.css';
const notyf = new Notyf();
export default notyf;

View File

@ -1,10 +1,20 @@
import { Context } from "../context/context";
import notyf from "./Notyf";
export default class Util {
public static range(size: number) {
var ans : number[] = [];
var ans: number[] = [];
for (let i = 0; i < size; i++) {
ans.push(i);
}
return ans;
}
public static checkConnectionAndMaybeAlert(context: Context): boolean {
if (context.rtmt.connectionState !== "connected") {
notyf.error(context.texts.ConnectionLost);
return true;
}
return false;
}
}

View File

@ -1067,10 +1067,10 @@
dependencies:
tslib "^2.4.0"
"@szhsin/react-menu@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@szhsin/react-menu/-/react-menu-3.0.2.tgz#d22971c53d56e6d404c9d3c98f533907cd8f03dc"
integrity sha512-m9Ly+cT+CxQx3xhq90CVaOLQWU7f7UKeMxfDt1gPYV23tDwEe8Zo6PO547qPlAEGEwwb9MdA38U8OyueXKJc2g==
"@szhsin/react-menu@^3.1.2":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@szhsin/react-menu/-/react-menu-3.1.2.tgz#3a791e7e6c672d113c298985bec5185e9c7aa8a7"
integrity sha512-NUnU429a3jXtRD4xxk8EsR4yRSuhZPWAkI+4P4K63LQPUZGVE7adVKtEmlyOpd8CRQ7aoUz1ZLr1VmR1nZi6GQ==
dependencies:
prop-types "^15.7.2"
react-transition-state "^1.1.4"
@ -1418,6 +1418,11 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
es6-object-assign@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz"
@ -1630,6 +1635,11 @@ node-releases@^2.0.6:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
notyf@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/notyf/-/notyf-3.10.0.tgz#67a64443c69ea0e6495c56ea0f91198860163d06"
integrity sha512-Mtnp+0qiZxgrH+TzVlzhWyZceHdAZ/UWK0/ju9U0HQeDpap1mZ8cC7H5wSI5mwgni6yeAjaxsTw0sbMK+aSuHw==
nth-check@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
@ -1738,6 +1748,11 @@ process@^0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
promise-polyfill@^6.0.2:
version "6.1.0"
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.1.0.tgz#dfa96943ea9c121fca4de9b5868cb39d3472e057"
integrity sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==
prop-types@^15.7.2:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
@ -1884,6 +1899,14 @@ svgo@^2.4.0, svgo@^2.5.0:
picocolors "^1.0.0"
stable "^0.1.8"
sweetalert@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/sweetalert/-/sweetalert-2.1.2.tgz#010baaa80d0dbdc86f96bfcaa96b490728594b79"
integrity sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==
dependencies:
es6-object-assign "^1.1.0"
promise-polyfill "^6.0.2"
term-size@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"