mirror of
				https://github.com/kevinveenbirkenbach/roulette-wheel.git
				synced 2025-11-04 02:58:01 +00:00 
			
		
		
		
	Add/remove/clear items
This commit is contained in:
		
							
								
								
									
										62
									
								
								app.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								app.css
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								index.html
									
									
									
									
									
								
							@@ -5,8 +5,21 @@
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Roulette wheel</title>
 | 
			
		||||
    <script type="module" src="dist/bundle.js"></script>
 | 
			
		||||
    <link rel="stylesheet" href="app.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    Test!
 | 
			
		||||
    <div id="menu">
 | 
			
		||||
        <img src="static/right-arrow.png" alt="open-close-arrow" class="openCloseMenu">
 | 
			
		||||
        <p id="counter">0/16</p>
 | 
			
		||||
        <div id="newItem">
 | 
			
		||||
            <label for="newItem">Item name</label>
 | 
			
		||||
            <input name="newItem" minlength="1" maxlength="32">
 | 
			
		||||
            <button id="newItemButton">Add</button>
 | 
			
		||||
            <button id="removeAllItemsButton">Remove all</button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <canvas></canvas>
 | 
			
		||||
    <footer><div>Icons made by <a href="https://www.flaticon.com/authors/roundicons" title="Roundicons">Roundicons</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div></footer>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										99
									
								
								src/app.ts
									
									
									
									
									
								
							
							
						
						
									
										99
									
								
								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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/components/base-component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/components/base-component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
export abstract class Component<T extends HTMLElement> {
 | 
			
		||||
    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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								src/components/menu-delete-item.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/components/menu-delete-item.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import { Component } from "./base-component";
 | 
			
		||||
 | 
			
		||||
export class DeleteItemEl extends Component<HTMLImageElement>{
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								src/components/menu-item-list.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/components/menu-item-list.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import { Component } from "./base-component";
 | 
			
		||||
import { ItemEl } from "./menu-item";
 | 
			
		||||
 | 
			
		||||
export class ItemElList extends Component<HTMLUListElement> {
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/components/menu-item.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/components/menu-item.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
import { Component } from "./base-component";
 | 
			
		||||
import { DeleteItemEl } from "./menu-delete-item";
 | 
			
		||||
 | 
			
		||||
export class ItemEl extends Component<HTMLLIElement> {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								static/close.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/close.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 6.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/right-arrow.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/right-arrow.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.5 KiB  | 
@@ -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/
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ module.exports = {
 | 
			
		||||
    module: {
 | 
			
		||||
        rules: [
 | 
			
		||||
            {
 | 
			
		||||
                test: /\.ts?$/,
 | 
			
		||||
                test: /\.tsx?$/,
 | 
			
		||||
                use: 'ts-loader',
 | 
			
		||||
                exclude: /node_modules/
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user