add OPTIONS page, add size, difficult level mode

This commit is contained in:
Sergey Krylov 2022-07-25 17:44:48 +05:00
parent 1a778e680d
commit e2c70835e2
21 changed files with 431 additions and 222 deletions

View File

@ -4,4 +4,5 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 20px; padding: 20px;
font-family: "Pacifico", sans-serif;
} }

View File

@ -1,7 +1,8 @@
import MatchGame from 'components/MatchGame/MatchGame'; import MatchGame from 'components/../pages/MatchGame/MatchGame';
import MatchMenu from 'components/MatchMenu/MatchMenu'; import MatchMenu from 'components/../pages/MatchMenu/MatchMenu';
import { useMatchGame } from 'context/MatchGameContext'; import { useMatchGame } from 'context/MatchGameContext';
import React from 'react'; import React from 'react';
import Options from 'src/pages/Options/Options';
const AppRoutes = () => { const AppRoutes = () => {
const {currentMenuItem} = useMatchGame(); const {currentMenuItem} = useMatchGame();
@ -17,6 +18,9 @@ const AppRoutes = () => {
case 'new-game': case 'new-game':
return <MatchGame isNewGame={true}/>; return <MatchGame isNewGame={true}/>;
case 'options':
return <Options/>
default: return <MatchMenu/>; default: return <MatchMenu/>;
} }

View File

@ -1,99 +0,0 @@
import { useMatchGame } from 'context/MatchGameContext';
import React, { useEffect, useState } from 'react';
import Button from 'components/UI/Button/Button';
import Timer from 'components/UI/Timer/Timer';
import cls from './BottomPanel.module.scss';
type BottomPanelProps = {
startGame: () => void;
retryGame: () => void;
isFinishedGame?: boolean;
shuffleCards: () => void;
isNewGame: boolean;
}
type PanelState = 'start' | 'retry';
const BottomPanel: React.FC<BottomPanelProps> = (props) => {
const [isTimerStarted, setIsTimerStarted] = useState(false);
const [isTimerStopped, setIsTimerStopped] = useState(false);
const [panelState, setPanelState] = useState<PanelState>('start');
const {gameTime, changeGameTime, setCurrentMenuItem, setIsGameStarted} = useMatchGame();
function onStartBtnClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
setIsTimerStarted(true);
props.startGame();
}
useEffect(() => {
if (props.isFinishedGame) {
setIsTimerStopped(true);
localStorage.removeItem('match-game:cards');
}
setPanelState(props.isFinishedGame ? 'retry' : 'start');
}, [props.isFinishedGame])
function onRetryBtnClickHandler() {
setCurrentMenuItem?.('new-game');
setIsTimerStarted(false);
setIsTimerStopped(false);
setPanelState('start');
props.retryGame?.();
}
function renderRetryStatePanel() {
return (
<>
<Button onClick={goToMenu}>&#8678;</Button>
<Button onClick={onRetryBtnClickHandler}>RETRY</Button>
</>
)
}
function onShuffleBtnClickHandler() {
props.shuffleCards?.();
}
function goToMenu() {
setIsGameStarted?.(false);
setCurrentMenuItem?.('menu');
}
function renderStartStatePanel() {
return isTimerStarted
? <>
<Button onClick={goToMenu}>&#8678;</Button>
<Timer isStarted={isTimerStarted} startTime={gameTime || 0} isFinished={isTimerStopped} changeTime={changeGameTime}/>
</>
: <>
<Button onClick={goToMenu}>&#8678;</Button>
<Button onClick={onStartBtnClickHandler}>{props.isNewGame ? 'START' : 'Continue'}</Button>
{props.isNewGame && <Button onClick={onShuffleBtnClickHandler}>SHUFFLE</Button>}
</>
}
function renderPanelByState(state: PanelState) {
if (!state) {
console.error('Нет состояния у панели')
return;
}
switch (state) {
case 'start': return renderStartStatePanel();
case 'retry': return renderRetryStatePanel();
default: return;
}
}
return (
<div className={cls.BottomPanel}>
{ renderPanelByState(panelState) }
</div>
);
};
export default BottomPanel;

View File

@ -115,7 +115,7 @@ const CardsField: React.FC<CardsFieldProps> = ({cards, isGameStarted = false, fi
useEffect(() => { useEffect(() => {
setCardsState(prev => (prev.map(el => ({...el, disabled: !isGameStarted, toggled: !isGameStarted && isNewGame})))); setCardsState(prev => (prev.map(el => ({...el, disabled: !isGameStarted, toggled: !isGameStarted && isNewGame}))));
if (isGameStarted) localStorage.setItem('match-game:cards', JSON.stringify(cardsState)); if (isGameStarted) localStorage.setItem('match-game:cards', JSON.stringify(cardsState));
}, [isGameStarted]) }, [isGameStarted, isNewGame])
useEffect(() => { useEffect(() => {
if (isNewGame) setCardsState(cards.map(el => createCard(el))) if (isNewGame) setCardsState(cards.map(el => createCard(el)))

View File

@ -1,6 +1,29 @@
.FinishedField { .FinishedField {
background: #283593;
border-radius: 10px;
border: 2px solid #fff;
box-sizing: border-box;
color: #ffffff; color: #ffffff;
max-width: 320px;
margin: 0 auto;
font-family: 'Pacifico', sans-serif; font-family: 'Pacifico', sans-serif;
height: 500px;
margin: 0 auto;
padding: 10px 50px;
position: relative;
width: 500px;
h1 {
text-align: center;
font-size: 40px;
}
p {
display: flex;
justify-content: space-between;
font-size: 24px;
span {
font-style: italic;
font-weight: bold;
}
}
} }

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { DiffLevel, FieldSize } from 'src/pages/Options/Options';
import { getTimeString } from 'src/utils'; import { getTimeString } from 'src/utils';
import cls from './FinishedField.module.scss'; import cls from './FinishedField.module.scss';
@ -6,16 +7,18 @@ import cls from './FinishedField.module.scss';
type FinishedFieldProps = { type FinishedFieldProps = {
mistakesCount?: number; mistakesCount?: number;
gameTime?: number; gameTime?: number;
difficultyLevel?: number;
fieldSize?: number;
} }
const FinishedField: React.FC<FinishedFieldProps> = ({mistakesCount, gameTime}) => { const FinishedField: React.FC<FinishedFieldProps> = ({mistakesCount, gameTime, difficultyLevel, fieldSize}) => {
return ( return (
<div className={cls.FinishedField}> <div className={cls.FinishedField}>
<h1>GAME IS FINISHED !</h1> <h1>GAME IS FINISHED !</h1>
<p>Время: <span>{getTimeString(new Date(gameTime || 0))}</span></p> <p>Время: <span>{getTimeString(new Date(gameTime || 0))}</span></p>
<p>Количество ошибок: <span>{mistakesCount}</span></p> <p>Количество ошибок: <span>{mistakesCount}</span></p>
<p>Сложность: <span>difficultyLevel</span></p> <p>Сложность: <span>{DiffLevel[difficultyLevel as any]}</span></p>
<p>Размер поля: <span>fieldSize</span></p> <p>Размер поля: <span>{FieldSize[fieldSize as any]}</span></p>
</div> </div>
); );
}; };

View File

@ -1,26 +0,0 @@
.FieldFinish {
width: 500px;
height: 500px;
background: #283593;
position: relative;
border: 2px solid #fff;
box-sizing: border-box;
padding: 10px;
border-radius: 10px;
h1 {
text-align: center;
font-size: 40px;
}
p {
display: flex;
justify-content: space-between;
font-size: 24px;
span {
font-style: italic;
font-weight: bold;
}
}
}

View File

@ -1,85 +0,0 @@
import React, { useEffect, useState } from 'react';
import BottomPanel from 'components/BottomPanel/BottomPanel';
import CardsField from 'components/CardsField/CardsField';
import FinishedField from 'components/FinishedField/FinishedField';
import { cardImages } from 'src/constants';
import { randomizeElementInArray } from 'src/utils';
import { useMatchGame } from 'context/MatchGameContext';
import cls from './MatchGame.module.scss'
interface MatchGameProps {
isNewGame: boolean;
}
const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
// TODO replace to CardsField
const [cards, setCards] = useState<any[]>(randomizeElementInArray([
{code: 'react', imageURL: cardImages.react},
{code: 'angular', imageURL: cardImages.angular},
{code: 'js', imageURL: cardImages.js},
{code: 'react', imageURL: cardImages.react},
{code: 'angular', imageURL: cardImages.angular},
{code: 'js', imageURL: cardImages.js},
]));
const {gameTime,
mistakesCount,
resetGameProgress,
setIsGameStarted,
setIsGameFinished,
isGameFinished,
isGameStarted
} = useMatchGame();
useEffect(() => {
if (isNewGame) resetGameProgress?.();
}, [isNewGame])
function startGame() {
setIsGameStarted?.(true);
}
function retryGame() {
setIsGameFinished?.(false);
setIsGameStarted?.(false);
setCards(prev => randomizeElementInArray(prev.map(el => ({...el, disabled: true, isMatched: false, toggled: true}))))
resetGameProgress?.();
}
function finishGame() {
setIsGameFinished?.(true);
localStorage.removeItem('match-game:cards');
}
// TODO replace to CardsField
function shuffleCards() {
setCards(randomizeElementInArray([...cards]));
}
function getWrapperClassList() {
let classes = [cls.Field];
if (isGameFinished) classes = [cls.FieldFinish];
return classes;
}
return (
<>
<div className={getWrapperClassList().join(' ')} style={{marginBottom: 20}}>
{
isGameFinished
? <FinishedField gameTime={gameTime} mistakesCount={mistakesCount}/>
: <CardsField cards={cards} isGameStarted={isGameStarted} finishGameCallback={finishGame} isNewGame={isNewGame}/>
}
</div>
<BottomPanel startGame={startGame} isFinishedGame={isGameFinished} retryGame={retryGame} shuffleCards={shuffleCards} isNewGame={isNewGame}/>
</>
);
};
export default MatchGame;

View File

@ -0,0 +1,15 @@
import React from 'react';
import cls from './BottomPanel.module.scss';
type BottomPanelProps = {}
const BottomPanel: React.FC<BottomPanelProps> = ({children}) => {
return (
<div className={cls.BottomPanel}>
{ children }
</div>
);
};
export default BottomPanel;

View File

@ -2,4 +2,6 @@ export const cardImages = {
react: 'https://w7.pngwing.com/pngs/79/518/png-transparent-js-react-js-logo-react-react-native-logos-icon-thumbnail.png', react: 'https://w7.pngwing.com/pngs/79/518/png-transparent-js-react-js-logo-react-react-native-logos-icon-thumbnail.png',
js: 'https://www.freepnglogos.com/uploads/javascript-png/javascript-logo-transparent-logo-javascript-images-3.png', js: 'https://www.freepnglogos.com/uploads/javascript-png/javascript-logo-transparent-logo-javascript-images-3.png',
angular: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/1200px-Angular_full_color_logo.svg.png', angular: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/1200px-Angular_full_color_logo.svg.png',
typescript: 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Typescript_logo_2020.svg/1024px-Typescript_logo_2020.svg.png',
webpack: 'https://cdn.iconscout.com/icon/free/png-256/webpack-3629741-3030792.png',
} }

View File

@ -1,5 +1,5 @@
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { getTimeString } from 'src/utils'; import { DiffLevel, FieldSize, OptionsType } from 'src/pages/Options/Options';
export type MenuItem = 'menu' | 'new-game' | 'continue-game' | 'options' | 'exit'; export type MenuItem = 'menu' | 'new-game' | 'continue-game' | 'options' | 'exit';
@ -15,6 +15,9 @@ interface MatchGameContextType {
setIsGameStarted?: (status: boolean) => void; setIsGameStarted?: (status: boolean) => void;
isGameFinished?: boolean; isGameFinished?: boolean;
setIsGameFinished?: (status: boolean) => void; setIsGameFinished?: (status: boolean) => void;
filedSize?: number;
diffLevel?: number;
saveOptions?: (options: OptionsType) => void;
} }
const MatchGameContext = React.createContext<MatchGameContextType>({}); const MatchGameContext = React.createContext<MatchGameContextType>({});
export const useMatchGame = () => useContext(MatchGameContext); export const useMatchGame = () => useContext(MatchGameContext);
@ -25,6 +28,8 @@ export const MatchGameProvider: React.FC = ({children}) => {
const [currentMenuItem, setCurrentMenuItem] = useState<MenuItem>('menu'); const [currentMenuItem, setCurrentMenuItem] = useState<MenuItem>('menu');
const [isGameStarted, setIsGameStarted] = useState(false); const [isGameStarted, setIsGameStarted] = useState(false);
const [isGameFinished, setIsGameFinished] = useState(false); const [isGameFinished, setIsGameFinished] = useState(false);
const [filedSize, setFieldSize] = useState(FieldSize.Small);
const [diffLevel, setDiffLevel] = useState(DiffLevel.Easy);
const addMistakesCount = () => { const addMistakesCount = () => {
return setMistakesCount(prev => prev + 1) return setMistakesCount(prev => prev + 1)
@ -39,6 +44,12 @@ export const MatchGameProvider: React.FC = ({children}) => {
setGameTime(0); setGameTime(0);
setIsGameStarted(false); setIsGameStarted(false);
setIsGameFinished(false); setIsGameFinished(false);
localStorage.removeItem('match-game:cards');
}
const saveOptions = (options: OptionsType) => {
setFieldSize(options.size);
setDiffLevel(options.diffLevel);
} }
return ( return (
@ -53,7 +64,10 @@ export const MatchGameProvider: React.FC = ({children}) => {
isGameStarted, isGameStarted,
setIsGameStarted, setIsGameStarted,
isGameFinished, isGameFinished,
setIsGameFinished setIsGameFinished,
filedSize,
diffLevel,
saveOptions
}}> }}>
{children} {children}
</MatchGameContext.Provider> </MatchGameContext.Provider>

View File

@ -0,0 +1,129 @@
import CardsField from 'components/CardsField/CardsField';
import FinishedField from 'components/FinishedField/FinishedField';
import BottomPanel from 'components/UI/BottomPanel/BottomPanel';
import Button from 'components/UI/Button/Button';
import Timer from 'components/UI/Timer/Timer';
import { useMatchGame } from 'context/MatchGameContext';
import React, { useEffect, useState } from 'react';
import { createCards, randomizeElementInArray } from 'src/utils';
interface MatchGameProps {
isNewGame: boolean;
}
type PanelState = 'start' | 'retry';
const MatchGame: React.FC<MatchGameProps> = ({isNewGame}) => {
const {
gameTime,
mistakesCount,
resetGameProgress,
setIsGameStarted,
setIsGameFinished,
isGameFinished,
isGameStarted,
changeGameTime,
setCurrentMenuItem,
diffLevel,
filedSize
} = useMatchGame();
// TODO replace to CardsField
const [cards, setCards] = useState<any[]>(randomizeElementInArray(createCards(diffLevel, filedSize)));
const [isTimerStarted, setIsTimerStarted] = useState(false);
const [isTimerStopped, setIsTimerStopped] = useState(false);
const [panelState, setPanelState] = useState<PanelState>('start');
useEffect(() => {
if (isNewGame) resetGameProgress?.();
}, [isNewGame, resetGameProgress])
useEffect(() => {
if (isGameFinished) {
setIsTimerStopped(true);
localStorage.removeItem('match-game:cards');
}
setPanelState(isGameFinished ? 'retry' : 'start');
}, [isGameFinished])
function startGame() {
setIsGameStarted?.(true);
setIsTimerStarted(true);
}
function retryGame() {
setIsGameFinished?.(false);
setIsGameStarted?.(false);
setIsTimerStarted?.(false);
setCards(prev => randomizeElementInArray(prev.map(el => ({...el, disabled: true, isMatched: false, toggled: true}))))
resetGameProgress?.();
}
function finishGame() {
setIsGameFinished?.(true);
localStorage.removeItem('match-game:cards');
}
function shuffleCards() {
setCards(randomizeElementInArray([...cards]));
}
function goToMenu() {
setIsGameStarted?.(false);
setCurrentMenuItem?.('menu');
}
function renderRetryStatePanel() {
return (
<>
<Button onClick={goToMenu}>&#8678;</Button>
<Button onClick={retryGame}>RETRY</Button>
</>
)
}
function renderStartStatePanel() {
return isTimerStarted
? <>
<Button onClick={goToMenu}>&#8678;</Button>
<Timer isStarted={isTimerStarted} startTime={gameTime || 0} isFinished={isTimerStopped} changeTime={changeGameTime}/>
</>
: <>
<Button onClick={goToMenu}>&#8678;</Button>
<Button onClick={startGame}>{isNewGame ? 'START' : 'Continue'}</Button>
{isNewGame && <Button onClick={shuffleCards}>SHUFFLE</Button>}
</>
}
function renderPanelByState(state: PanelState) {
if (!state) {
console.error('Нет состояния у панели')
return;
}
switch (state) {
case 'start': return renderStartStatePanel();
case 'retry': return renderRetryStatePanel();
default: return;
}
}
return (
<>
<div style={{marginBottom: 20}}>
{
isGameFinished
? <FinishedField gameTime={gameTime} mistakesCount={mistakesCount} difficultyLevel={diffLevel} fieldSize={filedSize}/>
: <CardsField cards={cards} isGameStarted={isGameStarted} finishGameCallback={finishGame} isNewGame={isNewGame}/>
}
</div>
<BottomPanel>
{ renderPanelByState(panelState) }
</BottomPanel>
</>
);
};
export default MatchGame;

View File

@ -18,6 +18,7 @@ const MatchMenu: React.FC<GameMenuProps> = () => {
return ( return (
<div> <div>
<h2>Match game menu</h2>
<ul> <ul>
{ {
canContinueGame() ? <li onClick={() => clickItemHandler('continue-game')}>Continue</li> : null canContinueGame() ? <li onClick={() => clickItemHandler('continue-game')}>Continue</li> : null

View File

@ -0,0 +1,28 @@
import Button from 'components/UI/Button/Button';
import React from 'react';
import cls from './Options.module.scss'
interface OptionProps {
text: string;
value: string;
onChange: (type: 'next' | 'prev') => void;
}
const Option:React.FC<OptionProps> = ({value, text, onChange}) => {
return (
<div className={cls.Option}>
<div>
{text}
</div>
<div className={cls.selectGroup}>
<Button className={cls.button} onClick={() => onChange('prev')}>&#10094;</Button>
<span>{value}</span>
<Button className={cls.button} onClick={() => onChange('next')}>&#10095;</Button>
</div>
</div>
);
};
export default Option;

View File

@ -0,0 +1,48 @@
.Options {
width: 500px;
height: 500px;
background: #283593;
position: relative;
border: 2px solid #fff;
box-sizing: border-box;
padding: 10px;
border-radius: 10px;
color: #ffffff;
margin-bottom: 20px;
ul {
list-style: none;
margin: 0;
}
h2 {
text-align: center;
}
}
.Option {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 30px;
.button {
width: 45px;
height: 45px;
margin: 10px 5px;
}
.selectGroup {
display: flex;
align-items: center;
justify-content: space-between;
span {
display: block;
width: 100px;
text-align: center;
}
}
}

View File

@ -0,0 +1,72 @@
import BottomPanel from 'components/UI/BottomPanel/BottomPanel';
import Button from 'components/UI/Button/Button';
import { useMatchGame } from 'context/MatchGameContext';
import React, { useState } from 'react';
import Option from 'src/pages/Options/Option';
import cls from './Options.module.scss'
export type OptionsType = {
diffLevel: number;
size: number;
}
export enum DiffLevel {
Easy,
Normal,
Hard
}
export enum FieldSize {
Small,
Normal,
Big
}
const Options: React.FC = () => {
const {saveOptions, setCurrentMenuItem, diffLevel, resetGameProgress, filedSize} = useMatchGame();
const [size, setSize] = useState(filedSize || FieldSize.Small);
const [difficultLevel, setDifficultLevel] = useState(diffLevel || DiffLevel.Easy);
function saveOptionHandler() {
saveOptions?.({
diffLevel: difficultLevel,
size
});
resetGameProgress?.();
setCurrentMenuItem?.('menu');
}
function changeFieldSize(type: 'prev' | 'next') {
const size = Object.values(FieldSize).length/2;
if (type === 'next') setSize(prev => prev + 1 === size ? 0 : prev + 1);
if (type === 'prev') setSize(prev => prev === 0 ? size - 1 : prev - 1);
}
function changeDiffLevel(type: 'prev' | 'next') {
const size = Object.values(DiffLevel).length/2;
if (type === 'next') setDifficultLevel(prev => prev + 1 === size ? 0 : prev + 1);
if (type === 'prev') setDifficultLevel(prev => prev === 0 ? size - 1 : prev - 1);
}
return (
<>
<div className={cls.Options}>
<h2>Options</h2>
<ul>
<li><Option text={'Size'} value={FieldSize[size]} onChange={changeFieldSize}/></li>
<li><Option text={'Difficult level'} value={DiffLevel[difficultLevel]} onChange={changeDiffLevel}/></li>
</ul>
</div>
<BottomPanel>
<Button onClick={() => setCurrentMenuItem?.('menu')}>&#8678;</Button>
<Button onClick={saveOptionHandler}>Save</Button>
</BottomPanel>
</>
);
};
export default Options;

View File

@ -1,5 +1,10 @@
import { cardImages } from 'src/constants';
import { DiffLevel, FieldSize } from 'src/pages/Options/Options';
export type CardType = {code: string, imageURL: string};
export function randomizeElementInArray (arr: any[]) { export function randomizeElementInArray (arr: any[]) {
return arr.sort(() => { return [...arr].sort(() => {
return Math.random() > 0.5 ? 1 : -1; return Math.random() > 0.5 ? 1 : -1;
}) })
} }
@ -16,3 +21,77 @@ export function getTimeString(time?: Date): string {
return `${minutes}:${seconds}` return `${minutes}:${seconds}`
} }
export function createCards(diffLevel: DiffLevel = DiffLevel.Easy, size: FieldSize = FieldSize.Small): CardType[] {
let arrSize = 6;
switch (diffLevel) {
case DiffLevel.Easy:
switch (size) {
case FieldSize.Small: {
arrSize = 6;
break;
}
case FieldSize.Normal:{
arrSize = 12;
break;
}
case FieldSize.Big:{
arrSize = 18;
break;
}
}
return Array(arrSize).fill('').map((_, i) => {
if (i < arrSize / 3) return {code: 'js', imageURL: cardImages.js};
if (i >= arrSize / 3 && i < arrSize * 2/3) return {code: 'react', imageURL: cardImages.react};
return {code: 'angular', imageURL: cardImages.angular}
});
case DiffLevel.Normal:
switch (size) {
case FieldSize.Small: {
arrSize = 8;
break;
}
case FieldSize.Normal:{
arrSize = 16;
break;
}
case FieldSize.Big:{
arrSize = 24;
break;
}
}
return Array(arrSize).fill('').map((_, i) => {
if (i < arrSize / 4) return {code: 'js', imageURL: cardImages.js};
if (i >= arrSize / 4 && i < arrSize / 2) return {code: 'react', imageURL: cardImages.react};
if (i >= arrSize / 2 && i < arrSize * 3/4) return {code: 'angular', imageURL: cardImages.angular};
return {code: 'typescript', imageURL: cardImages.typescript}
});
case DiffLevel.Hard:
switch (size) {
case FieldSize.Small: {
arrSize = 10;
break;
}
case FieldSize.Normal:{
arrSize = 20;
break;
}
case FieldSize.Big:{
arrSize = 30;
break;
}
}
return Array(arrSize).fill('').map((_, i) => {
if (i < arrSize / 5) return {code: 'js', imageURL: cardImages.js};
if (i >= arrSize / 5 && i < arrSize * 2/5) return {code: 'react', imageURL: cardImages.react};
if (i >= arrSize * 2/5 && i < arrSize * 3/5) return {code: 'angular', imageURL: cardImages.angular};
if (i >= arrSize * 3/5 && i < arrSize * 4/5) return {code: 'typescript', imageURL: cardImages.typescript};
return {code: 'webpack', imageURL: cardImages.webpack}
});
}
return Array(6).fill({code: 'angular', imageURL: cardImages.angular});
}