diff --git a/app.css b/app.css new file mode 100644 index 0000000..1cbcd7e --- /dev/null +++ b/app.css @@ -0,0 +1,62 @@ +html { + font-family: 'Courier New', Courier, monospace; +} + +canvas { + margin: 0; + position: absolute; + width: 500px; + height: 500px; + top: 50%; + left: 50%; + margin-right: -50%; + transform: translate(-50%, -50%); +} + +input { + height: 1.5rem; + width: 15rem; +} + +ul { + list-style-type: none; + padding: 0; + text-align: center; +} + +li { + padding: 0.75rem; +} + +.openCloseMenu { + width: 1rem; + height: 1rem; +} + +.item-delete { + width: 1rem; + height: 1rem; + float: right; +} + +#menu { + height: 100vh; + width: 20rem; +} + +#newItemButton { + height: 2rem; + width: 10rem; + margin-top: 0.5rem; +} + +#menu-item { + text-align: center; +} + +#counter { + font-size: 24px; + text-align: center; +} + + diff --git a/index.html b/index.html index 85b43a0..f19797d 100644 --- a/index.html +++ b/index.html @@ -5,8 +5,21 @@ Roulette wheel + + - Test! + + + \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index e69de29..ac6fa6b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -0,0 +1,99 @@ +import { ItemEl } from "./components/menu-item"; +import { ItemElList } from "./components/menu-item-list"; + +const canvas = document.querySelector("canvas")! as HTMLCanvasElement; +const ctx = canvas.getContext("2d")!; +const centreX = canvas.width / 2; +const centreY = canvas.height / 2; + +ctx.beginPath(); +ctx.moveTo(centreX, centreY); +ctx.stroke(); + +console.log("Works!"); + +const maxItems = 16; +const items: ItemElList = new ItemElList(); + +const menuEl = document.getElementById("menu")! as HTMLDivElement; +const counterEl = document.getElementById("counter")!; +const itemNameInput = document.querySelector("input")! as HTMLInputElement; +const newItemButtonEl = document.getElementById( + "newItemButton" +)! as HTMLButtonElement; +const removeAllItemsButtonEl = document.getElementById( + "removeAllItemsButton" +)! as HTMLButtonElement; + +menuEl.appendChild(items.el); + +const avaliableRGBs = ["255, 0, 0", "0, 255, 0", "0, 0, 255"]; +const bgOpacity = 0.5; +let nextColor = avaliableRGBs[0]; + +let counter = 0; + +const idsPool: number[] = []; +initalizeIdsPool(); + +function addNewItem() { + if (itemNameInput.value.length > 0 && itemNameInput.value.length <= 32) { + if (counter < maxItems && idsPool.length > 0) { + const item = new ItemEl( + idsPool.pop()!, + itemNameInput.value, + `rgba(${nextColor}, ${bgOpacity})` + ); + + item.deleteItem.el.addEventListener("click", () => { + const itemElId = calculateItemElId(item); + idsPool.push(itemElId); + setNextColor(); + items.removeItem(item); + counter--; + updateCounter(); + }); + + items.addItem(item); + counter++; + updateCounter(); + setNextColor(); + } else { + alert("Maximum number of items!"); + } + } +} + +function removeAllItems() { + if (items.itemsLength > 0) { + items.clear(); + counter = 0; + initalizeIdsPool(); + nextColor = avaliableRGBs[0]; + updateCounter(); + } else { + alert("Nothing to clear!"); + } +} + +function updateCounter() { + counterEl.textContent = `${counter}/${maxItems}`; +} + +function setNextColor() { + nextColor = avaliableRGBs[counter % avaliableRGBs.length]; +} + +function calculateItemElId(item: ItemEl) { + return +item.el.id.slice(0, item.el.id.indexOf("-")); +} + +function initalizeIdsPool() { + idsPool.length = 0; + for (let i = 0; i < maxItems; i++) { + idsPool.push(i); + } +} + +newItemButtonEl.addEventListener("click", addNewItem); +removeAllItemsButtonEl.addEventListener("click", removeAllItems); diff --git a/src/components/base-component.ts b/src/components/base-component.ts new file mode 100644 index 0000000..921615f --- /dev/null +++ b/src/components/base-component.ts @@ -0,0 +1,15 @@ +export abstract class Component { + el: T; + + constructor(el: T){ + this.el = el; + } + + removeChildren() { + if(this.el.childNodes.length > 0){ + while(this.el.firstChild){ + this.el.removeChild(this.el.firstChild); + } + } + } +} \ No newline at end of file diff --git a/src/components/menu-delete-item.ts b/src/components/menu-delete-item.ts new file mode 100644 index 0000000..ef0601c --- /dev/null +++ b/src/components/menu-delete-item.ts @@ -0,0 +1,17 @@ +import { Component } from "./base-component"; + +export class DeleteItemEl extends Component{ + + constructor(id: number){ + const el = document.createElement('img'); + el.src = 'static/close.png'; + el.alt = 'delete'; + el.className = 'item-delete' + el.id = `${id}-delete`; + super(el); + } + + configure(){ + // TODO: make icon rotation after mouse over event + } +} \ No newline at end of file diff --git a/src/components/menu-item-list.ts b/src/components/menu-item-list.ts new file mode 100644 index 0000000..0fcbcfe --- /dev/null +++ b/src/components/menu-item-list.ts @@ -0,0 +1,40 @@ +import { Component } from "./base-component"; +import { ItemEl } from "./menu-item"; + +export class ItemElList extends Component { + private items: ItemEl[]; + + constructor(){ + const UList = document.createElement('ul'); + UList.className = 'item-list'; + super(UList); + this.items = []; + } + + get itemsLength() { + return this.items.length; + } + + addItem(item: ItemEl){ + this.items.push(item); + this.el.appendChild(item.el); + } + + clear(){ + this.removeChildren(); + this.items = []; + } + + removeItem(item: ItemEl){ + item.el.remove(); + const indexToRemove = this.items.indexOf(item); + this.items.splice(indexToRemove, 1); + } + + findLastItem(){ + if(this.items.length > 0){ + return this.items[this.items.length - 1]; + } + return null; + } +} diff --git a/src/components/menu-item.ts b/src/components/menu-item.ts new file mode 100644 index 0000000..0c031d0 --- /dev/null +++ b/src/components/menu-item.ts @@ -0,0 +1,19 @@ +import { Component } from "./base-component"; +import { DeleteItemEl } from "./menu-delete-item"; + +export class ItemEl extends Component { + + deleteItem: DeleteItemEl; + + constructor(id: number, value: string, color: string){ + const deleteItem = new DeleteItemEl(id); + const el = document.createElement('li'); + el.className = 'menu-item'; + el.id = `${id}-item`; + el.textContent = value; + el.style.backgroundColor = color; + el.appendChild(deleteItem.el); + super(el); + this.deleteItem = deleteItem; + } +} \ No newline at end of file diff --git a/static/close.png b/static/close.png new file mode 100644 index 0000000..45e82a4 Binary files /dev/null and b/static/close.png differ diff --git a/static/right-arrow.png b/static/right-arrow.png new file mode 100644 index 0000000..f224f62 Binary files /dev/null and b/static/right-arrow.png differ diff --git a/webpack.config.js b/webpack.config.js index 5a551f7..5edbeac 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,18 +1,18 @@ const path = require('path'); module.exports = { - mode: "development", + mode: 'development', entry: './src/app.ts', output: { - path:path.resolve(__dirname, "dist"), - filename: "bundle.js", - publicPath: 'dist' + path:path.resolve(__dirname, 'dist'), + filename: 'bundle.js', + publicPath: '/dist/' }, devtool: 'inline-source-map', module: { rules: [ { - test: /\.ts?$/, + test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ } diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 40bc575..0a9ec75 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -11,7 +11,7 @@ module.exports = { module: { rules: [ { - test: /\.ts?$/, + test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }