Edition de la catégorie
-
-
- Nom des zones de combat (séparée par des ';')
+ setLice(e.target.value)}/>
@@ -373,6 +650,22 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
+ {state.id !== undefined && }
}
diff --git a/src/main/webapp/src/pages/competition/editor/CMTChronoPanel.jsx b/src/main/webapp/src/pages/competition/editor/CMTChronoPanel.jsx
index 40ac209..2c744b5 100644
--- a/src/main/webapp/src/pages/competition/editor/CMTChronoPanel.jsx
+++ b/src/main/webapp/src/pages/competition/editor/CMTChronoPanel.jsx
@@ -9,7 +9,7 @@ export function ChronoPanel() {
})
const [chrono, setChrono] = useState({time: 0, startTime: 0})
const chronoText = useRef(null)
- const state = useRef({chronoState: 0, countBlink: 20, lastColor: "black", lastTimeStr: "00:00"})
+ const state = useRef({chronoState: 0, countBlink: 20, lastColor: "#000000", lastTimeStr: "00:00"})
const publicAffDispatch = usePubAffDispatch();
const addTime = (time) => setChrono(prev => ({...prev, time: prev.time - time}))
@@ -34,12 +34,12 @@ export function ChronoPanel() {
const timer = setInterval(() => {
let currentDuration = config.time
- let color = "black"
+ let color = "#000000"
if (state_.chronoState === 1) {
- color = (state_.countBlink < blinkRfDuration) ? "black" : "red"
+ color = (state_.countBlink < blinkRfDuration) ? "#000000" : "#ff0000"
} else if (state_.chronoState === 2) {
currentDuration = (state_.chronoState === 0) ? 10000 : config.pause
- color = (state_.countBlink < blinkRfDuration) ? "green" : "red"
+ color = (state_.countBlink < blinkRfDuration) ? "#008000" : "#ff0000"
}
const timeStr = timePrint(currentDuration - getTime())
diff --git a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx
index 4b8e048..a4522be 100644
--- a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx
+++ b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx
@@ -11,6 +11,7 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faCircleQuestion} from "@fortawesome/free-regular-svg-icons";
import {toast} from "react-toastify";
import "./CMTMatchPanel.css"
+import {useOBS} from "../../../hooks/useOBS.jsx";
function CupImg() {
return
![]()
{
const categoryListener = ({data}) => {
setCats([...cats.filter(c => c.id !== data.id), data])
}
+ const sendAddCategory = ({data}) => {
+ setCats([...cats, data])
+ }
+ const sendDelCategory = ({data}) => {
+ if (catId === data)
+ setCatId(-1);
+ setCats([...cats.filter(c => c.id !== data)])
+ }
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
- return () => dispatch({type: 'removeListener', payload: categoryListener})
+ dispatch({type: 'addListener', payload: {callback: sendAddCategory, code: 'sendAddCategory'}})
+ dispatch({type: 'addListener', payload: {callback: sendDelCategory, code: 'sendDelCategory'}})
+ return () => {
+ dispatch({type: 'removeListener', payload: categoryListener})
+ dispatch({type: 'removeListener', payload: sendAddCategory})
+ dispatch({type: 'removeListener', payload: sendDelCategory})
+ }
}, [cats]);
const cat = cats?.find(c => c.id === catId);
+ useEffect(() => {
+ setText("poule",cat ? cat.name : "");
+ }, [cat, connected]);
+
return <>
Catégorie
@@ -131,10 +151,15 @@ function ListMatch({cat, matches, trees, menuActions}) {
const [type, setType] = useState(1);
useEffect(() => {
+ if (!cat)
+ return;
if ((cat.type & type) === 0)
setType(cat.type);
}, [cat]);
+ if (!cat)
+ return <>>;
+
return
{cat && cat.type === 3 && <>
@@ -173,6 +198,10 @@ function MatchList({matches, cat, menuActions}) {
.map(m => ({...m, win: win(m.scores)}))
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
+ const isActiveMatch = (index) => {
+ return liceName.length === 1 || (liceName[(index - firstIndex) % liceName.length] === lice)
+ }
+
const match = matches.find(m => m.id === activeMatch)
useEffect(() => {
if (!match) {
@@ -183,7 +212,7 @@ function MatchList({matches, cat, menuActions}) {
payload: {
c1: match.c1,
c2: match.c2,
- next: marches2.filter((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice && m.id !== activeMatch).map(m => ({
+ next: marches2.filter((m, index) => !m.end && isActiveMatch(index) && m.id !== activeMatch).map(m => ({
c1: m.c1,
c2: m.c2
}))
@@ -198,7 +227,7 @@ function MatchList({matches, cat, menuActions}) {
useEffect(() => {
if (match && match.poule !== lice)
- setActiveMatch(marches2.find((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice)?.id)
+ setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id)
}, [lice]);
useEffect(() => {
@@ -207,12 +236,12 @@ function MatchList({matches, cat, menuActions}) {
if (marches2.some(m => m.id === activeMatch))
return;
- setActiveMatch(marches2.find((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice)?.id);
+ setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id);
}, [matches])
return <>
{liceName.length > 1 &&
-
-
+
+
{
setLice(e.target.value);
localStorage.setItem("cm_lice", e.target.value);
@@ -224,11 +253,11 @@ function MatchList({matches, cat, menuActions}) {
}
-
+
- | L |
+ Z |
P |
N° |
|
@@ -240,7 +269,7 @@ function MatchList({matches, cat, menuActions}) {
{marches2.map((m, index) => (
setActiveMatch(m.id)}>
|
{liceName[(index - firstIndex) % liceName.length]} |
@@ -259,7 +288,7 @@ function MatchList({matches, cat, menuActions}) {
- {activeMatch &&
}
+ {activeMatch &&
}
>
}
@@ -325,27 +354,29 @@ function BuildTree({treeData, matches, menuActions}) {
return
-
-
+
{currentMatch?.matchSelect &&
- }
+ }
}
-function ScorePanel({matchId, match, menuActions}) {
+function ScorePanel({matchId, matchs, match, menuActions}) {
const onClickVoid = useRef(() => {
});
return
-
+
}
-function ScorePanel_({matchId, match, menuActions, onClickVoid_}) {
+function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) {
const {sendRequest} = useWS()
const setLoading = useLoadingSwitcher()
@@ -355,6 +386,11 @@ function ScorePanel_({matchId, match, menuActions, onClickVoid_}) {
const tableRef = useRef(null)
const scoreRef = useRef([])
const lastScoreClick = useRef(null)
+ const scoreInRef = useRef(null)
+
+ useEffect(() => {
+ scoreInRef.current = scoreIn;
+ }, [scoreIn]);
useEffect(() => {
menuActions.current.saveScore = (scoreRed, scoreBlue) => {
@@ -403,9 +439,12 @@ function ScorePanel_({matchId, match, menuActions, onClickVoid_}) {
const {matchId, round, comb} = lastScoreClick.current;
lastScoreClick.current = null;
- const scoreIn_ = String(scoreIn).trim() === "" ? -1000 : Number(scoreIn);
+ const scoreIn_ = String(scoreInRef.current).trim() === "" ? -1000 : Number(scoreInRef.current);
+
+ const score = matchs.find(m => m.id === matchId).scores.find(s => s.n_round === round);
+
+ console.log("Updating score", matchId, round, comb, scoreIn_, score);
- const score = match.scores.find(s => s.n_round === round);
let newScore;
if (score) {
if (comb === 1)
@@ -474,6 +513,18 @@ function ScorePanel_({matchId, match, menuActions, onClickVoid_}) {
setEnd(match.end);
}, [match]);
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (inputRef.current && !inputRef.current.contains(event.target)) {
+ onClickVoid();
+ }
+ };
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, []);
+
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const o = [...tooltipTriggerList]
o.map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
diff --git a/src/main/webapp/src/pages/competition/editor/CMTable.jsx b/src/main/webapp/src/pages/competition/editor/CMTable.jsx
index e9e2ddb..287b77e 100644
--- a/src/main/webapp/src/pages/competition/editor/CMTable.jsx
+++ b/src/main/webapp/src/pages/competition/editor/CMTable.jsx
@@ -1,16 +1,20 @@
import React, {useEffect, useRef, useState} from "react";
import {useRequestWS} from "../../../hooks/useWS.jsx";
-import {useCombsDispatch} from "../../../hooks/useComb.jsx";
+import {useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {createPortal} from "react-dom";
import {copyStyles} from "../../../utils/copyStyles.js";
-import {PubAffProvider, usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
+import {PubAffProvider, usePubAffDispatch, usePubAffState} from "../../../hooks/useExternalWindow.jsx";
import {faArrowRightArrowLeft, faDisplay} from "@fortawesome/free-solid-svg-icons";
import {PubAffWindow} from "./PubAffWindow.jsx";
import {SimpleIconsScore} from "../../../assets/SimpleIconsScore.ts";
import {ChronoPanel} from "./CMTChronoPanel.jsx";
import {CategorieSelect} from "./CMTMatchPanel.jsx";
import {PointPanel} from "./CMTPoint.jsx";
+import {importOBSConfiguration, OBSProvider, useOBS} from "../../../hooks/useOBS.jsx";
+import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
+import {timePrint} from "../../../utils/Tools.js";
+import {toast} from "react-toastify";
export function CMTable() {
const combDispatch = useCombsDispatch()
@@ -24,39 +28,39 @@ export function CMTable() {
combDispatch({type: 'SET_ALL', payload: {source: "register", data: data}});
}, [data]);
- return
-
-
-
-
-
Chronomètre
-
-
+ return
+
+
-
+
+
}
const windowName = "FFSAFScorePublicWindow";
@@ -68,6 +72,9 @@ function Menu({menuActions}) {
const publicAffDispatch = usePubAffDispatch()
const [showPubAff, setShowPubAff] = useState(false)
const [showScore, setShowScore] = useState(true)
+ const {connected, connect, disconnect} = useOBS();
+ const longPress = useRef({time: null, timer: null, button: null});
+ const obsModal = useRef(null);
const externalWindow = useRef(null)
const containerEl = useRef(document.createElement("div"))
@@ -76,11 +83,6 @@ function Menu({menuActions}) {
if (sessionStorage.getItem(windowName + "_open") === "true") {
handlePubAff();
}
- //return () => {
- // if (!externalWindow.current)
- // return;
- // externalWindow.current.close();
- //}
}, []);
const handlePubAff = __ => {
@@ -118,15 +120,76 @@ function Menu({menuActions}) {
menuActions.current.switchSore?.();
}
+ const longTimeAction = (button) => {
+ if (button === "obs") {
+ obsModal.current.click();
+ }
+ }
+
+ const longPressDown = (button) => {
+ longPress.current.button = button;
+ longPress.current.time = new Date();
+ longPress.current.timer = setTimeout(() => {
+ longTimeAction(button);
+
+ longPress.current.time = null;
+ longPress.current.button = null;
+ }, 1000);
+ }
+
+ const longPressUp = (button) => {
+ clearTimeout(longPress.current.timer);
+
+ if (longPress.current.time) {
+ const diff = new Date() - longPress.current.time;
+ if (longPress.current.button === button) {
+ if (diff >= 1000) {
+ longTimeAction(button);
+ } else {
+ if (button === "obs") {
+ if (connected) {
+ disconnect();
+ } else {
+ importOBSConfiguration()
+ .then(config => {
+ connect("ws://" + config.adresse + "/", config.password, config.assets_dir);
+ })
+ .catch(() => {
+ toast.error("Aucune configuration OBS trouvée, veuillez en importer une");
+ });
+ }
+ }
+ }
+ }
+
+ longPress.current.time = null;
+ longPress.current.button = null;
+ }
+ }
+
+ const handleOBSSubmit = (e) => {
+ e.preventDefault();
+ const form = e.target;
+ const prefix = form[0].value;
+
+ sessionStorage.setItem("obs_prefix", prefix);
+ }
+
if (!e)
return <>>;
return <>
{createPortal(
<>
-
+
longPressDown("obs")}
+ onMouseUp={() => longPressUp("obs")}
+ data-bs-toggle="tooltip2" data-bs-placement="top"
+ data-bs-title="Clique court : Charger la configuration et se connecter. Clique long : Configuration de la lice"/>
>, document.getElementById("actionMenu"))}
{externalWindow.current && createPortal(, containerEl.current)}
+
+
+
+
+
+
+
Configuration OBS
+
+
+
+
+
+
>
}
+
+function ObsAutoSyncWhitPubAff() {
+ const {connected, setText, setTextAndColor, setDiapo} = useOBS();
+ const oldState = useRef({timeColor: "#000000", timeStr: "--:--", c1: null, c2: null, showScore: true, scoreRouge: 0, scoreBleu: 0});
+ const state = usePubAffState();
+ const {getComb} = useCombs();
+
+ useEffect(() => {
+ if (state.c1 !== oldState.current.c1) {
+ const comb = getComb(state.c1);
+ setText("comb.rouge", comb ? (comb?.fname + " " + comb?.lname) : "");
+ const files = []
+ if (comb?.club_uuid) files.push(`club_${comb.club_uuid}.png`)
+ if (comb?.country) files.push(`flag_${comb.country.toLowerCase()}.png`)
+ setDiapo("img.rouge", files);
+ oldState.current.c1 = state.c1;
+ }
+
+ if (state.c2 !== oldState.current.c2) {
+ const comb = getComb(state.c2);
+ setText("comb.blue", comb ? (comb?.fname + " " + comb?.lname) : "");
+ const files = []
+ if (comb?.club_uuid) files.push(`club_${comb.club_uuid}.png`)
+ if (comb?.country) files.push(`flag_${comb.country.toLowerCase()}.png`)
+ setDiapo("img.blue", files);
+ oldState.current.c2 = state.c2;
+ }
+
+ if (state.showScore !== oldState.current.showScore) {
+ setText("score.rouge", state.showScore ? state.scoreRouge.toString() : "");
+ setText("score.blue", state.showScore ? state.scoreBleu.toString() : "");
+ oldState.current.showScore = state.showScore;
+ }
+
+ if (state.showScore === undefined || state.showScore) {
+ if (state.scoreRouge !== oldState.current.scoreRouge) {
+ setText("score.rouge", (state.scoreRouge || 0).toString());
+ oldState.current.scoreRouge = state.scoreRouge;
+ }
+ if (state.scoreBleu !== oldState.current.scoreBleu) {
+ setText("score.blue", (state.scoreBleu || 0).toString());
+ oldState.current.scoreBleu = state.scoreBleu;
+ }
+ }
+ }, [state]);
+
+ state.timeCb2 = (payload) => {
+ if (payload.timeStr && payload.timeColor) {
+ setTextAndColor("temps", payload.timeStr, payload.timeColor === "#000000" ? "#ffffff" : payload.timeColor);
+
+ oldState.current.timeStr = payload.timeStr;
+ oldState.current.lastColor = payload.timeColor;
+ }
+ }
+
+ useEffect(() => {
+ if (!connected)
+ return;
+ // Initial sync
+ const comb = getComb(oldState.current.c1);
+ const comb2 = getComb(oldState.current.c2);
+ setText("comb.rouge", comb ? (comb?.fname + " " + comb?.lname) : "");
+ setText("comb.blue", comb2 ? (comb2?.fname + " " + comb2?.lname) : "");
+ setTextAndColor("temps", oldState.current.timeStr, oldState.current.timeColor === "#000000" ? "#ffffff" : oldState.current.timeColor);
+ setText("score.rouge", oldState.current.showScore === undefined || oldState.current.showScore ? oldState.current.scoreRouge.toString() : "");
+ setText("score.blue", oldState.current.showScore === undefined || oldState.current.showScore ? oldState.current.scoreBleu.toString() : "");
+
+ }, [connected])
+}
diff --git a/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx b/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx
index 7df2e9d..faae064 100644
--- a/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx
+++ b/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx
@@ -17,13 +17,15 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faTrash} from "@fortawesome/free-solid-svg-icons";
import {win} from "../../../utils/Tools.js";
+const vite_url = import.meta.env.VITE_URL;
+
function CupImg() {
return
}
-export function CategoryContent({cat, catId, setCat}) {
+export function CategoryContent({cat, catId, setCat, menuActions}) {
const setLoading = useLoadingSwitcher()
const {sendRequest, dispatch} = useWS();
const [matches, reducer] = useReducer(MarchReducer, []);
@@ -47,10 +49,10 @@ export function CategoryContent({cat, catId, setCat}) {
const treeListener = ({data}) => {
if (!cat || data.length < 1 || data[0].categorie !== cat.id)
return
- setCat({
- ...cat,
- trees: data.map(d => from_sendTree(d, true))
- })
+ setCat(cat_ => ({
+ ...cat_,
+ trees: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
+ }))
let matches2 = [];
let combsToAdd = [];
@@ -64,10 +66,16 @@ export function CategoryContent({cat, catId, setCat}) {
reducer({type: 'UPDATE_OR_ADD', payload: {...data, c1: data.c1?.id, c2: data.c2?.id}});
combDispatch({type: 'SET_ALL', payload: {source: "match", data: [data.c1, data.c2].filter(d => d != null)}});
- if (data.c1 !== null && !groupsRef.current.some(g => g.id === data.c1?.id))
- setGroups(prev => [...prev, {id: data.c1?.id, poule: data.poule}]);
- if (data.c2 !== null && !groupsRef.current.some(g => g.id === data.c2?.id))
- setGroups(prev => [...prev, {id: data.c2?.id, poule: data.poule}]);
+ setGroups(prev => {
+ if (data.c1 !== null && !prev.some(g => g.id === data.c1?.id))
+ return [...prev, {id: data.c1?.id, poule: data.poule}];
+ return prev;
+ })
+ setGroups(prev => {
+ if (data.c2 !== null && !prev.some(g => g.id === data.c2?.id))
+ return [...prev, {id: data.c2?.id, poule: data.poule}];
+ return prev;
+ })
}
}
@@ -103,7 +111,7 @@ export function CategoryContent({cat, catId, setCat}) {
name: data.name,
liceName: data.liceName,
type: data.type,
- trees: data.trees.map(d => from_sendTree(d, true))
+ trees: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
})
let matches2 = [];
@@ -142,7 +150,7 @@ export function CategoryContent({cat, catId, setCat}) {
return <>
{cat &&
}
@@ -150,7 +158,7 @@ export function CategoryContent({cat, catId, setCat}) {
>
}
-function AddComb({groups, setGroups, removeGroup}) {
+function AddComb({groups, setGroups, removeGroup, menuActions}) {
const {data, setData} = useRequestWS("getRegister", null)
const combDispatch = useCombsDispatch()
const {dispatch} = useWS()
@@ -184,6 +192,21 @@ function AddComb({groups, setGroups, removeGroup}) {
if (data === null)
return;
combDispatch({type: 'SET_ALL', payload: {source: "register", data: data}});
+
+ const resourceList = []
+ data.forEach(d => {
+ if (d.club_uuid) {
+ const url = `${vite_url}/api/club/${d.club_uuid}/logo`;
+ if (!resourceList.some(d => d.url === url))
+ resourceList.push({url: url, name: `club_${d.club_uuid}.png`});
+ }
+ if (d.country) {
+ const url = `/flags/svg/${d.country.toLowerCase()}.svg`;
+ if (!resourceList.some(d => d.url === url))
+ resourceList.push({url: url, name: `flag_${d.country.toLowerCase()}.svg`});
+ }
+ })
+ menuActions.current.resourceList = resourceList
}, [data]);
return <>
@@ -554,7 +577,7 @@ function MatchList({matches, cat, groups, reducer}) {
| N° |
Poule |
- Lice |
+ Zone |
|
Rouge |
Blue |
diff --git a/src/main/webapp/src/pages/competition/editor/CompetitionManagerRoot.jsx b/src/main/webapp/src/pages/competition/editor/CompetitionManagerRoot.jsx
index f47dbd3..59e7c3c 100644
--- a/src/main/webapp/src/pages/competition/editor/CompetitionManagerRoot.jsx
+++ b/src/main/webapp/src/pages/competition/editor/CompetitionManagerRoot.jsx
@@ -66,7 +66,7 @@ function HomeComp() {
}/>
- }/>
+ }/>
}/>
@@ -95,7 +95,7 @@ function WSStatus({setPerm}) {
return () => dispatch({type: 'removeListener', payload: welcomeListener})
}, [])
- return
+ return
{name}
Serveur:
diff --git a/src/main/webapp/src/pages/competition/editor/PubAffWindow.jsx b/src/main/webapp/src/pages/competition/editor/PubAffWindow.jsx
index 8eac0bc..0b7273d 100644
--- a/src/main/webapp/src/pages/competition/editor/PubAffWindow.jsx
+++ b/src/main/webapp/src/pages/competition/editor/PubAffWindow.jsx
@@ -14,16 +14,16 @@ const text2Style = {fontSize: "min(1.7vw, 7vh)", fontWeight: "bold"};
export function PubAffWindow({document}) {
const chronoText = useRef(null)
- const state2 = useRef({lastColor: "white", lastTimeStr: "--:--"})
+ const state2 = useRef({lastColor: "#ffffff", lastTimeStr: "--:--"})
const state = usePubAffState();
- document.title = "A React portal window"
+ document.title = "Affichage Public";
document.body.className = "bg-dark text-white overflow-hidden";
state.timeCb = (payload) => {
- state2.current = {lastColor: payload.timeColor === "black" ? "white" : payload.timeColor, lastTimeStr: payload.timeStr}
+ state2.current = {lastColor: payload.timeColor === "#000000" ? "#ffffff" : payload.timeColor, lastTimeStr: payload.timeStr}
chronoText.current.textContent = payload.timeStr
- chronoText.current.style.color = payload.timeColor === "black" ? "white" : payload.timeColor
+ chronoText.current.style.color = payload.timeColor === "#000000" ? "#ffffff" : payload.timeColor
}
const showScore = state.showScore ?? true;
diff --git a/src/main/webapp/src/utils/Tools.js b/src/main/webapp/src/utils/Tools.js
index 5821030..a74a8e3 100644
--- a/src/main/webapp/src/utils/Tools.js
+++ b/src/main/webapp/src/utils/Tools.js
@@ -153,3 +153,31 @@ export function timePrint(time, negSign = false) {
String(min).padStart(2, '0') + ":" +
String(sec).padStart(2, '0')
}
+
+//create full hex
+function fullHex (hex) {
+ let r = hex.slice(1,2);
+ let g = hex.slice(2,3);
+ let b = hex.slice(3,4);
+
+ r = parseInt(r+r, 16);
+ g = parseInt(g+g, 16);
+ b = parseInt(b+b, 16);
+
+ // return {r, g, b}
+ return { r, g, b };
+}
+
+//convert hex to rgb
+export function hex2rgb (hex) {
+ if(hex.length === 4){
+ return fullHex(hex);
+ }
+
+ const r = parseInt(hex.slice(1, 3), 16);
+ const g = parseInt(hex.slice(3, 5), 16);
+ const b = parseInt(hex.slice(5, 7), 16);
+
+ // return {r, g, b}
+ return { r, g, b };
+}