diff --git a/src/Interfaces/CallbackInterface.ts b/src/Interfaces/CallbackInterface.ts new file mode 100644 index 0000000..2315eb9 --- /dev/null +++ b/src/Interfaces/CallbackInterface.ts @@ -0,0 +1,11 @@ +export interface CallbackNoParam { + (): T; +} + +export interface CallbackOneParam { + (param1: T1): T2; +} + +export interface CallbackTwoParam { + (param1: T1, param2: T2): T3; +} diff --git a/src/Interfaces/CallbackNoParam.ts b/src/Interfaces/CallbackNoParam.ts deleted file mode 100644 index d41460b..0000000 --- a/src/Interfaces/CallbackNoParam.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface CallbackNoParam { - (): T; -} diff --git a/src/Interfaces/CallbackOneParamInterface.ts b/src/Interfaces/CallbackOneParamInterface.ts deleted file mode 100644 index 6a6a897..0000000 --- a/src/Interfaces/CallbackOneParamInterface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface CallbackOneParam { - (param1: T1): T2; -} diff --git a/src/Interfaces/PlayerDTOInterface.ts b/src/Interfaces/PlayerDTOInterface.ts index b59e8ce..263e510 100644 --- a/src/Interfaces/PlayerDTOInterface.ts +++ b/src/Interfaces/PlayerDTOInterface.ts @@ -2,4 +2,5 @@ export interface PlayerDTO { name: string; path: string; color: string; + isAI: boolean; } diff --git a/src/app/EmptyView.ts b/src/app/EmptyView.ts new file mode 100644 index 0000000..4d16f3b --- /dev/null +++ b/src/app/EmptyView.ts @@ -0,0 +1,25 @@ +import { Render } from './utils/Render'; + +export abstract class EmptyView { + protected view: HTMLElement; + protected viewContainer: HTMLElement; + + constructor(isHidden = true) { + this.viewContainer = Render.elementFactory('div', { + className: 'view__container', + }); + this.view = Render.elementFactory( + 'div', + { className: `view${isHidden ? ' hidden' : ''}` }, + this.viewContainer, + ); + } + + hide(): void { + this.view.classList.add('hidden'); + } + + show(): void { + this.view.classList.remove('hidden'); + } +} diff --git a/src/app/MenuView.ts b/src/app/MenuView.ts index 2a6a52f..b76d120 100644 --- a/src/app/MenuView.ts +++ b/src/app/MenuView.ts @@ -1,24 +1,30 @@ -import { ModeModal } from './components/ModeModal'; +import { PlayerDTO } from '~src/Interfaces/PlayerDTOInterface'; +import { EmptyView } from './EmptyView'; +import { ModeView } from './ModeView'; import { Render } from './utils/Render'; import { ViewController } from './ViewController'; -export class MenuView { - private modeModal: ModeModal; - constructor(private view: ViewController) { - this.modeModal = new ModeModal((players) => - this.view.launchGame(players), - ); - this.modeModal.hideModal(); - Render.render('body', this.modeModal.createModeModal()); +export class MenuView extends EmptyView { + private modeModal: ModeView; + constructor(private viewController: ViewController) { + super(false); + const backCallback = () => this.show(); + const submitCallback = ( + isDynamic: boolean, + players: PlayerDTO[], + ) => this.viewController.launchGame(players); + this.modeModal = new ModeView(backCallback, submitCallback); + Render.render('body', this.modeModal.theModeView); } displayMenu(): void { Render.removeAllChildren('#sf-app'); - Render.render( - '#sf-app', + Render.childrenInjector( + this.viewContainer, this.createLandingPage(), this.createFooter(), ); + Render.render('#sf-app', this.view); } private createLandingPage(): HTMLElement { @@ -81,7 +87,8 @@ export class MenuView { 'NEW GAME', ); startGameButton.addEventListener('click', () => { - this.modeModal.showModal(); + this.hide(); + this.modeModal.show(); }); const settingsButton = Render.elementFactory( 'button', diff --git a/src/app/components/ModeModal.ts b/src/app/ModeView.ts similarity index 67% rename from src/app/components/ModeModal.ts rename to src/app/ModeView.ts index 140e2a0..c6604e3 100644 --- a/src/app/components/ModeModal.ts +++ b/src/app/ModeView.ts @@ -1,31 +1,46 @@ -import { BasicModal } from './BasicModal'; -import { Render } from '../utils/Render'; -import { PlayerDTO } from '~src/Interfaces/PlayerDTOInterface'; -import { CallbackOneParam } from '~src/Interfaces/CallbackOneParamInterface'; +import { Render } from './utils/Render'; +import { + CallbackNoParam, + CallbackTwoParam, +} from '~src/Interfaces/CallbackInterface'; import { Avatars } from '~src/Enums/AvatarsEnum'; import { Colors } from '~src/Enums/ColorsEnum'; +<<<<<<< HEAD:src/app/components/ModeModal.ts import { check } from 'prettier'; export class ModeModal extends BasicModal { +======= +import { EmptyView } from './EmptyView'; +import { PlayerDTO } from '~src/Interfaces/PlayerDTOInterface'; + +export class ModeView extends EmptyView { +>>>>>>> feature/#116-mode-view:src/app/ModeView.ts private modeForm: HTMLFormElement; private addPlayerButton: HTMLElement; private removePlayerButton: HTMLElement; private playerInputsWrapper: HTMLElement; private backButton!: HTMLElement; private playButton!: HTMLElement; - private submitCallback: CallbackOneParam; + private submitCallback: CallbackTwoParam; + private backCallback: CallbackNoParam; /** - * @param submitCallback - will be called onSubmit with PlayerDTO[] data in the argument + * @param submitCallback - will be called onSubmit with isDynamic and PlayerDTO[] data in the arguments */ - constructor(submitCallback: CallbackOneParam) { + constructor( + backCallback: CallbackNoParam, + submitCallback: CallbackTwoParam, + ) { super(); this.addPlayerButton = Render.elementFactory( 'button', { type: 'button', - className: 'mode-form__add-player-btn', + className: 'mode-form__add-btn', }, - 'add next player', + Render.elementFactory('img', { + className: 'mode-form__add-btn-img', + src: '.\\static\\images\\ui\\plus-circle.svg', + }), ); this.removePlayerButton = Render.elementFactory( 'button', @@ -36,38 +51,65 @@ export class ModeModal extends BasicModal { 'remove player', ); this.playerInputsWrapper = Render.elementFactory('div', { - className: 'mode-inputs-wrapper', + className: 'mode-players-wrapper', }); this.modeForm = this.createForm(); + this.backCallback = backCallback; this.submitCallback = submitCallback; - } - /** - * Creates ModeModal and returns it as HTMLElement. - */ - createModeModal(): HTMLElement { - this.renderBasicModal( - 'Add your nick, choose avatar and color', - undefined, + Render.childrenInjector( + this.viewContainer, this.modeForm, this.generateButtons(), ); + this.addEventListeners(); - return this.modal; + } + + /** + * Returns ModeView as HTMLElement. + */ + get theModeView(): HTMLElement { + return this.view; } private createForm(): HTMLFormElement { - Render.childrenInjector(this.playerInputsWrapper); + const heading = Render.elementFactory( + 'h2', + { className: 'mode-view__heading' }, + 'Click to add new player', + ); + const mode = Render.elementFactory( + 'div', + { + className: 'mode-form__mode', + }, + Render.elementFactory('input', { + className: 'mode-form__mode-input', + type: 'checkbox', + id: 'mode', + name: 'mode', + }), + Render.elementFactory( + 'label', + { + className: 'mode-form__mode-input', + for: 'mode', + }, + 'dynamic mode', + ), + ); const form = Render.elementFactory( 'form', { id: 'mode-form', action: '', method: 'get', - className: 'mode-modal', + className: 'mode-form', }, - + heading, + mode, this.playerInputsWrapper, this.addPlayerButton, this.removePlayerButton, @@ -158,26 +200,62 @@ export class ModeModal extends BasicModal { type: 'text', id: indicator, name: indicator, +<<<<<<< HEAD:src/app/components/ModeModal.ts placeholder: `Janush ${numberOfPlayer}`, +======= + placeholder: 'type nickname here', +>>>>>>> feature/#116-mode-view:src/app/ModeView.ts className: 'mode-form__input', }); return input; } + private generateAICheckbox(numberOfPlayer: number): HTMLElement { + const indicator = `ai_${numberOfPlayer}`; + const checkbox = Render.elementFactory('input', { + className: 'mode-form__ai-input', + type: 'checkbox', + id: indicator, + name: indicator, + }); + const label = Render.elementFactory( + 'label', + { + className: 'mode-form__ai-label', + }, + 'AI Player', + ); + const aiWrapper = Render.elementFactory( + 'div', + { + className: 'mode-form__ai', + }, + checkbox, + label, + ); + return aiWrapper; + } + private generateColorInput(numberOfPlayer: number): HTMLElement { const colorInputs: HTMLElement[] = Object.values(Colors).reduce( (colorsElements: HTMLElement[], value, index) => { const indicator = `${numberOfPlayer}colorChoice_${index + 1}`; const label = Render.elementFactory('label', { for: indicator, +<<<<<<< HEAD:src/app/components/ModeModal.ts className: `mode-form__color-label color-label${index + 1}`, +======= + className: 'mode-form__color-label', + style: `background-color: ${value}`, +>>>>>>> feature/#116-mode-view:src/app/ModeView.ts }); const radio = Render.elementFactory('input', { type: 'radio', name: `color_${numberOfPlayer}`, className: 'mode-form__color-radio', id: indicator, + required: '', value: value, }); if (index === 0) { @@ -226,6 +304,7 @@ export class ModeModal extends BasicModal { name: `path_${numberOfPlayer}`, className: 'mode-form__avatar-radio', id: indicator, + required: 'true', value: value, }); if (index === 0) { @@ -282,55 +361,82 @@ export class ModeModal extends BasicModal { ): HTMLElement { const fieldsWrapper = Render.elementFactory( 'div', +<<<<<<< HEAD:src/app/components/ModeModal.ts { className: 'add-player' }, +======= + { className: 'mode-form__add-player' }, +>>>>>>> feature/#116-mode-view:src/app/ModeView.ts this.generateNameInput(numberOfPlayer), this.generateAvatarInput(numberOfPlayer), this.generateColorInput(numberOfPlayer), this.generateAIPlayerInput(), ); + if (numberOfPlayer > 1) { + fieldsWrapper.appendChild( + this.generateAICheckbox(numberOfPlayer), + ); + } return fieldsWrapper; } private convertDataFormToPlayersData( formData: FormData, - ): PlayerDTO[] { - const playersData = []; + ): { isDynamic: boolean; players: PlayerDTO[] } { + const players: PlayerDTO[] = []; + let isDynamic = false; for (const [formKey, formValue] of formData.entries()) { + // TODO: remove before merge + console.log(formKey, formValue); + const value = formValue.toString(); const [key, numberOfPlayer] = formKey.split('_'); const index = +numberOfPlayer - 1; switch (key) { case 'name': { - playersData.push({ + players.push({ name: '', - path: Avatars.FARMER1, - color: Colors.GREEN, + path: '', + color: '', + isAI: false, } as PlayerDTO); - playersData[index].name = + players[index].name = value.trim().length > 0 ? value - : `Janush ${playersData.length}`; + : `Janush ${players.length}`; break; } case 'path': { - playersData[index].path = value; + players[index].path = value; break; } case 'color': { - playersData[index].color = value; + players[index].color = value; + break; + } + case 'ai': { + players[index].isAI = true; + break; + } + case 'mode': { + isDynamic = true; break; } } } - return playersData; + // TODO: remove before merge + console.log({ isDynamic, players }); + + return { isDynamic, players }; } private handleSubmit = (event: Event): void => { event.preventDefault(); const formData = new FormData(event.target as HTMLFormElement); - const playersData = this.convertDataFormToPlayersData(formData); - this.submitCallback(playersData); - this.hideModal(); + const { isDynamic, players } = this.convertDataFormToPlayersData( + formData, + ); + this.submitCallback(isDynamic, players); + this.hide(); this.modeForm.reset(); }; @@ -343,7 +449,8 @@ export class ModeModal extends BasicModal { }; private handleClickBackButton = (): void => { - this.hideModal(); + this.hide(); + this.backCallback(); this.modeForm.reset(); }; } diff --git a/src/app/components/TradeModal.ts b/src/app/components/TradeModal.ts index a9b9786..e479388 100644 --- a/src/app/components/TradeModal.ts +++ b/src/app/components/TradeModal.ts @@ -1,4 +1,4 @@ -import { CallbackNoParam } from '~src/Interfaces/CallbackNoParam'; +import { CallbackNoParam } from '~src/Interfaces/CallbackInterface'; import { AnimalNames } from '../../Enums/AnimalNamesEnum'; import { Player } from '../../Player'; import { Herd } from '../logic/Herd'; diff --git a/src/app/manuals/ModeModalManual.ts b/src/app/manuals/ModeModalManual.ts deleted file mode 100644 index c16b8c6..0000000 --- a/src/app/manuals/ModeModalManual.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ModeModal } from '../components/ModeModal'; -import { Render } from '../utils/Render'; - -export class ModeModalManual { - static playDemo(): void { - const modeModal = new ModeModal((playersData) => - console.log(playersData), - ); - - Render.render('#sf-app', modeModal.createModeModal()); - } -} diff --git a/src/app/manuals/ModeViewManual.ts b/src/app/manuals/ModeViewManual.ts new file mode 100644 index 0000000..bd99764 --- /dev/null +++ b/src/app/manuals/ModeViewManual.ts @@ -0,0 +1,16 @@ +import { PlayerDTO } from '~src/Interfaces/PlayerDTOInterface'; +import { ModeView } from '../ModeView'; +import { Render } from '../utils/Render'; + +export class ModeViewManual { + static playDemo(): void { + const backCallback = () => console.log('go back'); + const submitCallback = ( + isDynamic: boolean, + players: PlayerDTO[], + ) => console.log(isDynamic, players); + const modeView = new ModeView(backCallback, submitCallback); + + Render.render('#sf-app', modeView.theModeView); + } +} diff --git a/static/images/ui/plus-circle.svg b/static/images/ui/plus-circle.svg new file mode 100644 index 0000000..32d1cd2 --- /dev/null +++ b/static/images/ui/plus-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/styles/components/_components.scss b/styles/components/_components.scss index 1fef0f0..8c817fc 100644 --- a/styles/components/_components.scss +++ b/styles/components/_components.scss @@ -3,7 +3,8 @@ @import './components/button'; @import './components/modal'; @import './menu'; -@import './modeModal'; +@import './modeView'; @import './tradeModal'; @import './playerPanel'; @import './bankBoard'; +@import './emptyView'; diff --git a/styles/components/_emptyView.scss b/styles/components/_emptyView.scss new file mode 100644 index 0000000..0f6a33b --- /dev/null +++ b/styles/components/_emptyView.scss @@ -0,0 +1,4 @@ +.view { + &__container { + } +} diff --git a/styles/components/_modeModal.scss b/styles/components/_modeView.scss similarity index 88% rename from styles/components/_modeModal.scss rename to styles/components/_modeView.scss index 007d91d..797312c 100644 --- a/styles/components/_modeModal.scss +++ b/styles/components/_modeView.scss @@ -22,12 +22,21 @@ border-bottom: 1px solid #9ca4a4; text-align: center; } +<<<<<<< HEAD:styles/components/_modeModal.scss // Styles for avatars &__avatars { display: flex; flex-wrap: wrap; justify-content: center; +======= + &__color-wrapper { + display: flex; + } + &__color-label { + width: 50px; + height: 50px; +>>>>>>> feature/#116-mode-view:styles/components/_modeView.scss } &__avatar {