Newer
Older

Valentin Rigal
committed
<template>
<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 -->
:class="{
'is-selected':
selectedKey && selectedKey.row == i && selectedKey.column == j,
}"
v-on:mouseup="
char ? $emit('input', char) : $emit('input', { row: i, column: j })
"
<template v-if="char">{{ char.character }}</template>
<template v-else> </template>
<template v-if="resizable">
<label>Rows</label>
<!-- Directly parse field value to handle integers only -->
<input
:value="rows"
v-on:change.prevent="(e) => updateRows(e.target.value)"
:min="rowsCount"
:max="maxKeyboardSize"
type="number"
/>
<label>Columns</label>
<input
:value="columns"
v-on:change.prevent="(e) => updateColumns(e.target.value)"
:min="columnsCount"
:max="maxKeyboardSize"
type="number"
/>
</template>

Valentin Rigal
committed
</div>
</template>
<script>
import { mapState } from "vuex";
import { maxKeyboardSize } from "../config.js";

Valentin Rigal
committed
export default {
props: {
keyboardIndex: {
type: Number,

Valentin Rigal
committed
required: true,
},
// Row and column value of the selected keyboard key
selectedKey: {
type: Object,
default: null,
},
resizable: {
type: Boolean,
default: false,
},
data: () => ({
// Rows and columns including a potential resize of the keyboard
rows: 0,
columns: 0,
maxKeyboardSize,
}),
...mapState(["keyboards"]),
keyboard() {
return this.keyboards[this.keyboardIndex];
},
return this.keyboard && this.keyboard.characters;

Valentin Rigal
committed
},
// Return the number of rows from keyboard characters
if (!this.characters.length) return 1;
return (
this.characters && Math.max(...this.characters.map((c) => c.row)) + 1
);
// Return the number of columns from keyboard characters
if (!this.characters.length) return 1;
return (
this.characters && Math.max(...this.characters.map((c) => c.column)) + 1
);
const [tableRows, tableCols] = this.resizable
? [this.rows, this.columns]
: [this.rowsCount, this.columnsCount];
if (!tableRows || !tableCols) return;
const table = Array.from(Array(tableRows), () => new Array(tableCols));
if (!this.characters) return table;
this.characters.forEach((c) => {
table[c.row][c.column] = c;
});
return table;

Valentin Rigal
committed
},
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
methods: {
// We need to handle input changes manually as v-model.number allows random numbers or empty strings
updateRows(value) {
if (isNaN(value) || value < this.rowsCount || value > maxKeyboardSize)
return;
this.rows = parseInt(value);
},
updateColumns(value) {
if (isNaN(value) || value < this.columnsCount || value > maxKeyboardSize)
return;
this.columns = parseInt(value);
},
},
watch: {
rowsCount: {
immediate: true,
handler(n) {
if (!this.rows) this.rows = n || 1;
},
},
columnsCount: {
immediate: true,
handler(n) {
if (!this.columns) this.columns = n || 1;
},
},
},

Valentin Rigal
committed
};
</script>
<style scoped>
button {
width: 3rem;
height: 3rem;
margin: 0.1rem;

Valentin Rigal
committed
}
button.is-selected {
border: solid black 0.25rem;
border-radius: 0.25rem;
}