Merge pull request #10 from jhalitaksoy/new-ui

New UI
This commit is contained in:
Halit Aksoy 2022-06-04 22:43:17 +03:00 committed by GitHub
commit eb1a51c41f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 447 additions and 124 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "mancala-frontend", "name": "mancala-frontend",
"version": "0.1.3-beta.7", "version": "0.1.3-beta.8",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -14,6 +14,8 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@szhsin/react-menu": "^3.0.2",
"@types/": "szhsin/react-menu",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"mancala.js": "^0.0.2-beta.2", "mancala.js": "^0.0.2-beta.2",
"react": "^17.0.2", "react": "^17.0.2",

View File

@ -18,6 +18,10 @@ import { GameMove } from "./models/GameMove";
import PitAnimator from "./animation/PitAnimator"; import PitAnimator from "./animation/PitAnimator";
import BoardViewModel from "./viewmodel/BoardViewModel"; import BoardViewModel from "./viewmodel/BoardViewModel";
import { v4 } from "uuid"; import { v4 } from "uuid";
import { Menu, MenuButton, MenuItem } from "@szhsin/react-menu";
import "@szhsin/react-menu/dist/index.css";
import "@szhsin/react-menu/dist/transitions/slide.css";
import { getColorByLuminance } from "./util/ColorUtil";
type ConnectionState = "connecting" | "error" | "connected" | "reconnecting"; type ConnectionState = "connecting" | "error" | "connected" | "reconnecting";
@ -116,6 +120,12 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
}; };
}, []); }, []);
React.useEffect(() => {
context.themeManager.onThemeChange = () => {
updateBoardViewModel(pitAnimator.getBoardViewModelFromGame(game));
};
}, [boardViewModel]);
const resetGameState = () => { const resetGameState = () => {
setGame(undefined); setGame(undefined);
setCrashMessage(undefined); setCrashMessage(undefined);
@ -166,8 +176,9 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
const renderNewGameButton = () => { const renderNewGameButton = () => {
const newGame = ( const newGame = (
<Button <Button
context={context}
text={context.texts.NewGame} text={context.texts.NewGame}
color="#005f73" color={context.themeManager.theme.primary}
onClick={newGameClick} onClick={newGameClick}
/> />
); );
@ -184,14 +195,18 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
} }
return <></>; return <></>;
}; };
const menuTextColor = getColorByLuminance(
context.themeManager.theme.appBarBgColor,
context.themeManager.theme.primary,
context.themeManager.theme.primaryLight
);
return ( return (
<div <div
style={{ style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
background: "#EEEEEE", background: context.themeManager.theme.background,
flex: "1", flex: "1",
}} }}
> >
@ -205,8 +220,8 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
borderTopRightRadius: "1vw", borderTopRightRadius: "1vw",
minWidth: "10vw", minWidth: "10vw",
minHeight: "1vw", minHeight: "1vw",
background: "#2F2504", background: context.themeManager.theme.primary,
color: "white", color: context.themeManager.theme.primaryLight,
}} }}
> >
{connectionStateText()} {connectionStateText()}
@ -214,8 +229,8 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
)} )}
<div <div
style={{ style={{
padding: "0px 50px", padding: "0px 4vw",
background: "rgb(228, 228, 228)", background: context.themeManager.theme.appBarBgColor,
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
@ -223,15 +238,77 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
alignSelf: "stretch", alignSelf: "stretch",
}} }}
> >
<h1 style={{ margin: "10px 0px" }}>{context.texts.Mancala}</h1> <h1 style={{ color: menuTextColor, margin: "10px 0px" }}>
<div> {context.texts.Mancala}
</h1>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<div
style={{
marginRight: "1vw",
display: "flex",
alignItems: "center",
}}
>
<Menu
menuStyle={{
background: context.themeManager.theme.appBarBgColor,
}}
menuButton={
<span
style={{ color: menuTextColor }}
class="material-symbols-outlined"
>
light_mode
</span>
}
transition
align="end"
>
{context.themeManager.themes.map((theme) => {
return (
<MenuItem
style={{
color: menuTextColor,
}}
onMouseOver={(event) =>
(event.target.style.background =
context.themeManager.theme.background)
}
onMouseOut={(event) =>
(event.target.style.background = "transparent")
}
onClick={() => (context.themeManager.theme = theme)}
>
<div
style={{
borderRadius: "5vw",
background: theme.boardColor,
width: "1vw",
height: "1vw",
marginRight: "1vw",
}}
></div>
{theme.name}
</MenuItem>
);
})}
</Menu>
</div>
{renderNewGameButton()} {renderNewGameButton()}
{game && {game &&
!userKeyWhoLeave && !userKeyWhoLeave &&
!crashMessage && !crashMessage &&
(game?.state === "playing" || game?.state === "initial") && ( (game?.state === "playing" || game?.state === "initial") && (
<Button <Button
color="#005f73" context={context}
color={context.themeManager.theme.primary}
text={context.texts.Leave} text={context.texts.Leave}
onClick={leaveGame} onClick={leaveGame}
/> />
@ -239,6 +316,7 @@ const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
</div> </div>
</div> </div>
<InfoPanel <InfoPanel
context={context}
game={game} game={game}
crashMessage={crashMessage} crashMessage={crashMessage}
userKey={userKey} userKey={userKey}

View File

@ -12,6 +12,7 @@ import { v4 } from "uuid";
import { Context } from "../context"; import { Context } from "../context";
import BoardViewModelFactory from "../factory/BoardViewModelFactory"; import BoardViewModelFactory from "../factory/BoardViewModelFactory";
import { PitViewModelFactory } from "../factory/PitViewModelFactory"; import { PitViewModelFactory } from "../factory/PitViewModelFactory";
import { getColorByLuminance } from "../util/ColorUtil";
import BoardViewModel from "../viewmodel/BoardViewModel"; import BoardViewModel from "../viewmodel/BoardViewModel";
const animationUpdateInterval = 300; const animationUpdateInterval = 300;
@ -130,6 +131,11 @@ export default class PitAnimator {
pitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor; pitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
pitViewModel.stoneCount = 0; pitViewModel.stoneCount = 0;
} }
pitViewModel.stoneColor = getColorByLuminance(
pitViewModel.pitColor,
theme.stoneColor,
theme.stoneLightColor
);
} }
startAnimationUpdateCyle() { startAnimationUpdateCyle() {
@ -156,7 +162,7 @@ export default class PitAnimator {
} }
} }
private getBoardViewModelFromGame(game: MancalaGame): BoardViewModel { public getBoardViewModelFromGame(game: MancalaGame): BoardViewModel {
const pitViewModels = this.createPitViewModelsFromGame(game); const pitViewModels = this.createPitViewModelsFromGame(game);
return BoardViewModelFactory.create(v4(), pitViewModels); return BoardViewModelFactory.create(v4(), pitViewModels);
} }
@ -165,7 +171,7 @@ export default class PitAnimator {
return game.board.pits.map((pit) => { return game.board.pits.map((pit) => {
const theme = this.context.themeManager.theme; const theme = this.context.themeManager.theme;
const stoneCount = pit.stoneCount; const stoneCount = pit.stoneCount;
const stoneColor = theme.ballColor; const stoneColor = theme.stoneColor;
const pitColor = theme.holeColor; const pitColor = theme.holeColor;
const id = pit.index.toString(); const id = pit.index.toString();
return PitViewModelFactory.create({ return PitViewModelFactory.create({

View File

@ -2,6 +2,7 @@ import { Bank, MancalaGame, Pit } from "mancala.js";
import * as React from "react"; import * as React from "react";
import { FunctionComponent, useState } from "react"; import { FunctionComponent, useState } from "react";
import { Context } from "../context"; import { Context } from "../context";
import { getColorByLuminance } from "../util/ColorUtil";
import BoardViewModel from "../viewmodel/BoardViewModel"; import BoardViewModel from "../viewmodel/BoardViewModel";
import PitViewModel from "../viewmodel/PitViewModel"; import PitViewModel from "../viewmodel/PitViewModel";
@ -67,6 +68,11 @@ const StoreView: FunctionComponent<{
const balls = [...range(pitViewModel.stoneCount)].map((i) => ( const balls = [...range(pitViewModel.stoneCount)].map((i) => (
<BallView color={pitViewModel.stoneColor} /> <BallView color={pitViewModel.stoneColor} />
)); ));
const textColor = getColorByLuminance(
pitViewModel.pitColor,
context.themeManager.theme.primary,
context.themeManager.theme.primaryLight
);
return ( return (
<div <div
style={{ style={{
@ -91,7 +97,7 @@ const StoreView: FunctionComponent<{
fontFamily: "monospace", fontFamily: "monospace",
fontWeight: "bold", fontWeight: "bold",
fontSize: "2vw", fontSize: "2vw",
color: context.themeManager.theme.ballColor, color: textColor,
}} }}
> >
{balls.length} {balls.length}
@ -150,16 +156,14 @@ const BoardView: FunctionComponent<{
return ( return (
<div <div
style={{ style={{
margin: "10px", margin: "1vw",
padding: "20px", padding: "2vw",
display: "grid", display: "grid",
gridTemplateColumns: "repeat(8, 11vw)", gridTemplateColumns: "repeat(8, 11vw)",
gridTemplateRows: "repeat(2, 11vw)", gridTemplateRows: "repeat(2, 11vw)",
borderRadius: "3vw", borderRadius: "3vw",
transition: "background-color 0.5s", transition: "background-color 0.5s",
background: isUserTurn background: theme.boardColor,
? theme.boardColor
: theme.boardColorWhenPlayerTurn,
}} }}
> >
{userKey === game.player2Id ? ( {userKey === game.player2Id ? (

View File

@ -1,22 +1,34 @@
import * as React from 'react'; import * as React from "react";
import { FunctionComponent } from "react" import { FunctionComponent } from "react";
import { Context } from "../context";
import { getColorByLuminance } from "../util/ColorUtil";
const Button: FunctionComponent<{ text: String,onClick: () => void, color: string }> = ({ text, color, onClick }) => { const Button: FunctionComponent<{
context: Context;
text: String;
onClick: () => void;
color: string;
}> = ({ context, text, color, onClick }) => {
const textColor = getColorByLuminance(
color,
context.themeManager.theme.primary,
context.themeManager.theme.primaryLight
);
return (
<button
onClick={onClick}
style={{
background: color,
color: textColor,
margin: "5px",
padding: "10px",
border: "none",
borderRadius: "4vw",
}}
>
{text}
</button>
);
};
return ( export default Button;
<button
onClick={onClick}
style={{
background: color,
color : "white",
margin: "5px",
padding: "10px",
border : "none",
borderRadius: "3vw",
}} >
{text}
</button>
)
}
export default Button

View File

@ -1,66 +1,109 @@
import { MancalaGame } from 'mancala.js'; import { MancalaGame } from "mancala.js";
import * as React from 'react'; import * as React from "react";
import { FunctionComponent } from "react" import { FunctionComponent } from "react";
import { context } from '../context'; import { Context } from "../context";
import { getColorByLuminance } from "../util/ColorUtil";
function getInfoPanelTextByGameState(params: {
context: Context;
game: MancalaGame;
crashMessage: string;
userKey: string;
userKeyWhoLeave: string;
searchingOpponent: boolean;
}): string {
const {
context,
game,
crashMessage,
userKey,
userKeyWhoLeave,
searchingOpponent,
} = params;
if (searchingOpponent) {
return context.texts.SearchingOpponent + " " + context.texts.PleaseWait;
} else if (crashMessage) {
return context.texts.GameCrashed + " " + crashMessage;
} else if (userKeyWhoLeave) {
let message = context.texts.OpponentLeavesTheGame;
if (userKeyWhoLeave == userKey) {
message = context.texts.YouLeftTheGame;
}
return message;
} else if (game?.state == "ended") {
const wonPlayer = game.getWonPlayerId();
let whoWon =
game.getWonPlayerId() === userKey
? context.texts.YouWon
: context.texts.YouLost;
if (!wonPlayer) {
whoWon = context.texts.GameDraw;
}
return context.texts.GameEnded + " " + whoWon;
} else {
return game?.checkIsPlayerTurn(userKey)
? context.texts.YourTurn
: context.texts.OpponentTurn;
}
return undefined;
}
const InfoPanelContainer: FunctionComponent<{
context: Context;
color: string;
}> = (props) => {
return (
<div
style={{
background: props.color,
padding: "1vw 2vw",
marginTop: "1vw",
borderRadius: "10vw",
}}
>
{props.children}
</div>
);
};
const InfoPanel: FunctionComponent<{ const InfoPanel: FunctionComponent<{
game: MancalaGame, context: Context;
crashMessage: string, game: MancalaGame;
userKey: string, crashMessage: string;
userKeyWhoLeave: string, userKey: string;
searchingOpponent: boolean userKeyWhoLeave: string;
searchingOpponent: boolean;
}> = ({ }> = ({
game, crashMessage, userKey, userKeyWhoLeave, searchingOpponent }) => { context,
if (searchingOpponent) { game,
return ( crashMessage,
<h4>{ userKey,
context.texts.SearchingOpponent + " " + context.texts.PleaseWait userKeyWhoLeave,
}</h4> searchingOpponent,
) }) => {
} const isUserTurn = game?.checkIsPlayerTurn(userKey);
const containerColor = isUserTurn
? context.themeManager.theme.playerTurnColor
: context.themeManager.theme.holeColor;
const textColor = getColorByLuminance(
containerColor,
context.themeManager.theme.primary,
context.themeManager.theme.primaryLight
);
return (
<InfoPanelContainer context={context} color={containerColor}>
<h4 style={{ margin: "0", color: textColor }}>
{getInfoPanelTextByGameState({
context,
game,
crashMessage,
userKey,
userKeyWhoLeave,
searchingOpponent,
})}
</h4>
</InfoPanelContainer>
);
};
if (crashMessage) { export default InfoPanel;
return (
<h4>{
context.texts.GameCrashed + " " + crashMessage
}</h4>
)
}
if (userKeyWhoLeave) {
let message = context.texts.OpponentLeavesTheGame
if (userKeyWhoLeave == userKey) {
message = context.texts.YouLeftTheGame
}
return (
<h4>
{message}
</h4>
)
}
if (game) {
if (game.state == "ended") {
const wonPlayer = game.getWonPlayerId();
let whoWon = game.getWonPlayerId() === userKey ? context.texts.YouWon : context.texts.YouLost
if(!wonPlayer){
whoWon = context.texts.GameDraw
}
return (
<h4>{
context.texts.GameEnded + " " + whoWon
}</h4>
)
} else {
return (
<h4>{game.checkIsPlayerTurn(userKey) ? context.texts.YourTurn : context.texts.OpponentTurn}</h4>
)
}
}
return <h4></h4>
}
export default InfoPanel

View File

@ -2,7 +2,6 @@ import { Texts, TrTr } from "./const/texts";
import { RTMT } from "./rtmt/rtmt"; import { RTMT } from "./rtmt/rtmt";
import { RTMTWS } from "./rtmt/rtmt_websocket"; import { RTMTWS } from "./rtmt/rtmt_websocket";
import { UserKeyStore, UserKeyStoreImpl } from "./store/key_store"; import { UserKeyStore, UserKeyStoreImpl } from "./store/key_store";
import defaultTheme from "./theme/DefaultTheme";
import ThemeManager from "./theme/ThemeManager"; import ThemeManager from "./theme/ThemeManager";
export type Context = { export type Context = {
@ -16,7 +15,7 @@ export const initContext = () => {
const rtmt = new RTMTWS(); const rtmt = new RTMTWS();
const userKeyStore = new UserKeyStoreImpl(); const userKeyStore = new UserKeyStoreImpl();
const texts = TrTr; const texts = TrTr;
const themeManager = new ThemeManager(defaultTheme); const themeManager = new ThemeManager();
return { return {
rtmt: rtmt, rtmt: rtmt,
userKeyStore: userKeyStore, userKeyStore: userKeyStore,

View File

@ -11,6 +11,8 @@
<script src="./App.tsx"></script> <script src="./App.tsx"></script>
</body> </body>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" />
<style> <style>
html{ html{

32
src/theme/DarkTheme.ts Normal file
View File

@ -0,0 +1,32 @@
import { Theme } from "./Theme";
// from https://colorhunt.co/palette/0f0e0e5412128b9a46eeeeee
const colors = {
primary: "#541212",
secondary: "#0F0E0E",
tertiary: "#8B9A46",
quaternary: "#EEEEEE",
};
const colorSpecial = "#990000";
const darkTheme: Theme = {
id: "2",
name: "Dark Theme",
background: colors.primary,
appBarBgColor: colors.secondary,
primary: colors.primary,
primaryLight: colors.quaternary,
playerTurnColor: colors.secondary,
boardColor: colors.secondary,
holeColor: colors.tertiary,
pitSelectedColor: colors.tertiary,
stoneColor: colors.primary,
stoneLightColor: colors.tertiary,
pitGameMoveAnimateColor: colors.quaternary,
pitEmptyPitAnimateColor: colorSpecial,
pitLastStoneInBankPitAnimateColor: colorSpecial,
pitGetRivalStonePitAnimateColor: colorSpecial,
};
export default darkTheme;

View File

@ -1,19 +1,32 @@
import { Theme } from "./Theme"; import { Theme } from "./Theme";
// https://colorhunt.co/palette/7d5a50b4846ce5b299fcdec0
const colors = {
primary: "#7D5A50",
secondary: "#B4846C",
tertiary: "#E5B299",
quaternary: "#FCDEC0",
};
const colorSpecial = "#F6A9A9";
const defaultTheme: Theme = { const defaultTheme: Theme = {
id: "1",
name: "Default Light Theme",
background: "#EEEEEE", background: "#EEEEEE",
boardColor: "#4D606E", appBarBgColor: colors.quaternary,
boardColorWhenPlayerTurn: "#84b8a6", primary: colors.primary,
storeColor: "#3FBAC2", primaryLight: colors.quaternary,
storeColorWhenPlayerTurn: "#6cab94", playerTurnColor: colors.secondary,
holeColor: "#D3D4D8", boardColor: colors.secondary,
pitSelectedColor: "#8837fa", holeColor: colors.quaternary,
ballColor: "#393E46", pitSelectedColor: colors.tertiary,
ballLightColor: "#393E46", stoneColor: colors.primary,
pitGameMoveAnimateColor: "#c9b43c", stoneLightColor: colors.tertiary,
pitEmptyPitAnimateColor: "#5d7322", pitGameMoveAnimateColor: colors.tertiary,
pitLastStoneInBankPitAnimateColor: "#9463f7", pitEmptyPitAnimateColor: colorSpecial,
pitGetRivalStonePitAnimateColor: "#ff3d44", pitLastStoneInBankPitAnimateColor: colorSpecial,
pitGetRivalStonePitAnimateColor: colorSpecial,
}; };
export default defaultTheme; export default defaultTheme;

22
src/theme/OldTheme.ts Normal file
View File

@ -0,0 +1,22 @@
import { Theme } from "./Theme";
const oldTheme: Theme = {
id: "0",
name: "Old Theme",
background: "#EEEEEE",
appBarBgColor: "#e4e4e4",
primary: "#4D606E",
primaryLight: "#EEEEEE",
playerTurnColor: "#84b8a6",
boardColor: "#4D606E",
holeColor: "#D3D4D8",
pitSelectedColor: "#8837fa",
stoneColor: "#393E46",
stoneLightColor: "#EEEEEE",
pitGameMoveAnimateColor: "#c9b43c",
pitEmptyPitAnimateColor: "#5d7322",
pitLastStoneInBankPitAnimateColor: "#9463f7",
pitGetRivalStonePitAnimateColor: "#ff3d44",
};
export default oldTheme;

View File

@ -1,13 +1,16 @@
export type Theme = { export type Theme = {
id: string;
name: string;
primary: string;
primaryLight: string;
background: string; background: string;
appBarBgColor: string;
playerTurnColor: string;
boardColor: string; boardColor: string;
boardColorWhenPlayerTurn: string;
storeColor: string;
storeColorWhenPlayerTurn: string;
holeColor: string; holeColor: string;
pitSelectedColor: string; pitSelectedColor: string;
ballColor: string; stoneColor: string;
ballLightColor: string; stoneLightColor: string;
pitGameMoveAnimateColor: string; pitGameMoveAnimateColor: string;
pitEmptyPitAnimateColor: string; pitEmptyPitAnimateColor: string;
pitLastStoneInBankPitAnimateColor: string; pitLastStoneInBankPitAnimateColor: string;

View File

@ -1,18 +1,40 @@
import darkTheme from "./DarkTheme";
import defaultTheme from "./DefaultTheme";
import oldTheme from "./OldTheme";
import { Theme } from "./Theme"; import { Theme } from "./Theme";
export const themes = [defaultTheme, darkTheme, oldTheme];
const THEME_ID = "theme_id";
export default class ThemeManager { export default class ThemeManager {
_theme: Theme; _theme: Theme;
onThemeChange: (theme: Theme) => void; onThemeChange: (theme: Theme) => void;
constructor(theme: Theme) { constructor() {
this._theme = theme; this._theme = this.readFromLocalStorage() || defaultTheme;
} }
public get theme() { public get theme() {
return this._theme; return this._theme;
} }
public set theme(value) { public set theme(value: Theme) {
this._theme = value; this._theme = value;
this.onThemeChange?.(value); this.onThemeChange?.(value);
this.writetToLocalStorage(value);
}
private writetToLocalStorage(value: Theme) {
localStorage.setItem(THEME_ID, value.id);
}
private readFromLocalStorage(): Theme {
const themeID = localStorage.getItem(THEME_ID);
const theme = themes.find((eachTheme: Theme) => themeID === eachTheme.id);
return theme;
}
public get themes(): Theme[] {
return themes;
} }
} }

51
src/util/ColorUtil.ts Normal file
View File

@ -0,0 +1,51 @@
//from this gist https://gist.github.com/jfsiii/5641126
// from http://www.w3.org/TR/WCAG20/#relativeluminancedef
export function relativeLuminanceW3C(
R8bit: number,
G8bit: number,
B8bit: number
) {
const RsRGB = R8bit / 255;
const GsRGB = G8bit / 255;
const BsRGB = B8bit / 255;
const R =
RsRGB <= 0.03928 ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4);
const G =
GsRGB <= 0.03928 ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4);
const B =
BsRGB <= 0.03928 ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4);
// For the sRGB colorspace, the relative luminance of a color is defined as:
const L = 0.2126 * R + 0.7152 * G + 0.0722 * B;
return L;
}
export function relativeLuminanceW3CHexColor(hexColor: string): number {
const [r, g, b] = hexToRgb(hexColor);
return relativeLuminanceW3C(r, g, b);
}
// from https://www.codegrepper.com/code-examples/javascript/javascript+convert+color+string+to+rgb
export function rgbToHex(r: number, g: number, b: number): string {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
// from https://www.codegrepper.com/code-examples/javascript/javascript+convert+color+string+to+rgb
export function hexToRgb(
hex: string,
result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
): number[] {
return result ? result.map((i) => parseInt(i, 16)).slice(1) : null;
//returns [23, 14, 45] -> reformat if needed
}
export function getColorByLuminance(
color: string,
lightColor: string,
darkColor: string
): string {
return relativeLuminanceW3CHexColor(color) < 0.5 ? darkColor : lightColor;
}

View File

@ -1011,6 +1011,21 @@
"@parcel/utils" "^1.11.0" "@parcel/utils" "^1.11.0"
physical-cpu-count "^2.0.0" physical-cpu-count "^2.0.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==
dependencies:
prop-types "^15.7.2"
react-transition-state "^1.1.4"
"@types/@szhsin/react-menu":
version "3.0.2"
resolved "https://codeload.github.com/szhsin/react-menu/tar.gz/28284b2183801fb4f6a95e9270ce580441c5da70"
dependencies:
prop-types "^15.7.2"
react-transition-state "^1.1.4"
"@types/prop-types@*": "@types/prop-types@*":
version "15.7.5" version "15.7.5"
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz"
@ -3364,7 +3379,7 @@ log-symbols@^2.2.0:
dependencies: dependencies:
chalk "^2.0.1" chalk "^2.0.1"
loose-envify@^1.1.0: loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@ -4333,6 +4348,15 @@ process@^0.11.10:
resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
prop-types@^15.7.2:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.13.1"
psl@^1.1.28: psl@^1.1.28:
version "1.8.0" version "1.8.0"
resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz"
@ -4433,6 +4457,16 @@ react-dom@^17.0.2:
object-assign "^4.1.1" object-assign "^4.1.1"
scheduler "^0.20.2" scheduler "^0.20.2"
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-transition-state@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/react-transition-state/-/react-transition-state-1.1.4.tgz#113224eaa27e0ff81661305e44d5e0348cdf61ac"
integrity sha512-6nQLWWx95gYazCm6OdtD1zGbRiirvVXPrDtHAGsYb4xs9spMM7bA8Vx77KCpjL8PJ8qz1lXFGz2PTboCSvt7iw==
react@^17.0.2: react@^17.0.2:
version "17.0.2" version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"