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 http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
    <title>Roulette wheel</title>
 | 
					    <title>Roulette wheel</title>
 | 
				
			||||||
 | 
					    <script type="module" src="dist/bundle.js"></script>
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="app.css">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<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>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</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');
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
    mode: "development",
 | 
					    mode: 'development',
 | 
				
			||||||
    entry: './src/app.ts',
 | 
					    entry: './src/app.ts',
 | 
				
			||||||
    output: {
 | 
					    output: {
 | 
				
			||||||
        path:path.resolve(__dirname, "dist"),
 | 
					        path:path.resolve(__dirname, 'dist'),
 | 
				
			||||||
        filename: "bundle.js",
 | 
					        filename: 'bundle.js',
 | 
				
			||||||
        publicPath: 'dist'
 | 
					        publicPath: '/dist/'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    devtool: 'inline-source-map',
 | 
					    devtool: 'inline-source-map',
 | 
				
			||||||
    module: {
 | 
					    module: {
 | 
				
			||||||
        rules: [
 | 
					        rules: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                test: /\.ts?$/,
 | 
					                test: /\.ts$/,
 | 
				
			||||||
                use: 'ts-loader',
 | 
					                use: 'ts-loader',
 | 
				
			||||||
                exclude: /node_modules/
 | 
					                exclude: /node_modules/
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ module.exports = {
 | 
				
			|||||||
    module: {
 | 
					    module: {
 | 
				
			||||||
        rules: [
 | 
					        rules: [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                test: /\.ts?$/,
 | 
					                test: /\.tsx?$/,
 | 
				
			||||||
                use: 'ts-loader',
 | 
					                use: 'ts-loader',
 | 
				
			||||||
                exclude: /node_modules/
 | 
					                exclude: /node_modules/
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user