<template> <div> <div v-if="!charTable">An error occured loading the keyboard</div> <div v-else v-for="(row, i) in charTable" :key="i"> <span v-for="(char, j) in row" :key="j"> <!-- Emit a key of the keyboard or its position when it is empty --> <button :class="{ 'is-selected': selectedKey && selectedKey.row == i && selectedKey.column == j, }" type="button" v-on:mouseup=" char ? $emit('input', char) : $emit('input', { row: i, column: j }) " > <template v-if="char">{{ char.character }}</template> <template v-else> </template> </button> </span> </div> </div> </template> <script> export default { props: { keyboard: { type: Object, required: true, }, // Row and column value of the selected keyboard key selectedKey: { type: Object, default: null, }, }, computed: { characters() { return this.keyboard.characters; }, rowsCount() { return ( this.characters && Math.max(...this.characters.map((c) => c.row)) + 1 ); }, columnsCount() { return ( this.characters && Math.max(...this.characters.map((c) => c.column)) + 1 ); }, charTable() { if (!this.columnsCount || !this.rowsCount) return; // Return a list of every column with padding const table = Array.from( Array(this.rowsCount), () => new Array(this.columnsCount) ); if (!this.characters) return table; this.characters.forEach((c) => { table[c.row][c.column] = c; }); return table; }, }, }; </script> <style scoped> button { width: 3rem; height: 3rem; margin: 0.1rem; } button.is-selected { border: solid black 0.25rem; border-radius: 0.25rem; } </style>