Skip to content

Commit

Permalink
Merge pull request #649 from BabyElias/gsoc-table
Browse files Browse the repository at this point in the history
KTable - Week 2
  • Loading branch information
MisRob authored Jun 5, 2024
2 parents ba89898 + bce8877 commit 491fe19
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 68 deletions.
31 changes: 25 additions & 6 deletions docs/pages/playground.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
{ text: 'Level 2 ', link: { path: '#' } },
]"
/> -->
<KTable :headers="tableHeaders" :rows="tableRows" caption="User Information Table" />


<!-- Play around with your component here: -->

Expand All @@ -32,16 +34,33 @@
<script>
/*
Playground is a Vue component too,
so you can also use `data`, `methods`, etc.
as usual if helpful
*/
Playground is a Vue component too,
so you can also use `data`, `methods`, etc.
as usual if helpful
*/
import KTable from '../../lib/KTable/KTable.vue';
export default {
name: 'Playground',
components: {
KTable,
},
data() {
return {};
return {
tableHeaders: [
{ label: 'Name', dataType: 'string' },
{ label: 'Age', dataType: 'numeric' },
{ label: 'Birth Date', dataType: 'date' },
{ label: 'City', dataType: 'string' },
{ label: 'Misc', dataType: 'others' },
],
tableRows: [
['Alice', 25, '1999-05-12', 'New York', 'Example'],
['Bob', 30, '1994-02-22', 'San Francisco', 'Example'],
['Charlie', 35, '1989-12-15', 'Chicago', 'Example'],
],
};
},
methods: {},
};
</script>
60 changes: 0 additions & 60 deletions lib/KTable.vue

This file was deleted.

234 changes: 234 additions & 0 deletions lib/KTable/KTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
<template>

<div class="k-table">
<table>
<caption v-if="caption">
{{ caption }}
</caption>
<thead>
<tr>
<th
v-for="(header, index) in headers"
:key="index"
tabindex="0"
@click="handleSort(index)"
@keydown="handleKeydown($event, -1, index)"
>
{{ header.label }}
<span v-if="header.dataType !== 'others'" class="sort-icon">
<span v-if="sortKey === index && sortOrder === 'asc'">⬆️</span>
<span v-else-if="sortKey === index && sortOrder === 'desc'">⬇️</span>
<span v-else>⬍</span>
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in sortedRows" :key="rowIndex">
<KTableGridItem
v-for="(cell, cellIndex) in row"
:key="cellIndex"
:content="cell"
:align="headers[cellIndex].dataType === 'numeric' ? 'right' : 'left'"
:rowIndex="rowIndex"
:cellIndex="cellIndex"
@keydown="handleKeydown($event, rowIndex, cellIndex)"
/>
</tr>
</tbody>
</table>
</div>

</template>


<script>
import KTableGridItem from './KTableGridItem.vue';
export default {
name: 'KTable',
components: {
KTableGridItem,
},
props: {
headers: {
type: Array,
required: true,
},
rows: {
type: Array,
required: true,
},
caption: {
type: String,
required: false,
default: null,
},
},
data() {
return {
sortKey: null,
sortOrder: 'asc',
};
},
computed: {
sortedRows() {
if (this.sortKey === null) return this.rows;
const sortedRows = [...this.rows];
const sortHeader = this.headers[this.sortKey];
if (sortHeader.dataType === 'numeric') {
sortedRows.sort((a, b) => a[this.sortKey] - b[this.sortKey]);
} else if (sortHeader.dataType === 'string' || sortHeader.dataType === 'date') {
sortedRows.sort((a, b) => a[this.sortKey].localeCompare(b[this.sortKey]));
}
if (this.sortOrder === 'desc') {
sortedRows.reverse();
}
return sortedRows;
},
},
methods: {
handleSort(index) {
const clickedElement = event.target.tagName.toLowerCase();
if (clickedElement !== 'th' || this.headers[index].dataType === 'others') return;
if (this.sortKey === index) {
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
} else {
this.sortKey = index;
this.sortOrder = 'asc';
}
},
handleKeydown(event, rowIndex, cellIndex) {
const key = event.key;
const totalRows = this.rows.length;
const totalCols = this.headers.length;
let nextRowIndex = rowIndex;
let nextCellIndex = cellIndex;
switch (key) {
case 'ArrowUp':
if (rowIndex === 0) {
this.focusHeader(cellIndex);
return;
} else if (rowIndex > 0) {
nextRowIndex = rowIndex - 1;
} else {
return;
}
break;
case 'ArrowDown':
if (rowIndex === -1) {
nextRowIndex = 0;
} else {
nextRowIndex = (rowIndex + 1) % totalRows;
}
break;
case 'ArrowLeft':
if (rowIndex === -1) {
nextCellIndex = cellIndex > 0 ? cellIndex - 1 : totalCols - 1;
} else if (cellIndex > 0) {
nextCellIndex = cellIndex - 1;
} else {
nextCellIndex = totalCols - 1;
nextRowIndex = rowIndex > 0 ? rowIndex - 1 : -1; // Move to the header row if needed
}
break;
case 'ArrowRight':
if (rowIndex === -1) {
if (cellIndex < totalCols - 1) {
nextCellIndex = cellIndex + 1;
} else {
nextRowIndex = 0;
nextCellIndex = 0;
}
} else if (cellIndex < totalCols - 1) {
nextCellIndex = cellIndex + 1;
} else {
nextCellIndex = 0;
nextRowIndex = (rowIndex + 1) % totalRows;
}
break;
case 'Enter':
this.handleSort(cellIndex);
break;
default:
return;
}
this.focusCell(nextRowIndex, nextCellIndex);
event.preventDefault();
},
focusHeader(cellIndex) {
const nextHeader = this.$el.querySelector(`thead th:nth-child(${cellIndex + 1})`);
if (nextHeader) {
nextHeader.focus();
}
},
focusCell(rowIndex, cellIndex) {
let nextCell;
if (rowIndex === -1) {
nextCell = this.$el.querySelector(`thead th:nth-child(${cellIndex + 1})`);
} else {
nextCell = this.$el.querySelector(
`tbody tr:nth-child(${rowIndex + 1}) td:nth-child(${cellIndex + 1})`
);
}
if (nextCell) {
nextCell.focus();
}
},
},
};
</script>


<style scoped>
.k-table {
margin: 20px;
}
.k-table table {
width: 100%;
border: 2px solid #ccc;
border-collapse: separate;
border-spacing: 0;
}
.k-table th,
.k-table td {
padding: 15px;
text-align: left;
position: relative;
}
.k-table th {
background-color: #f2f2f2;
border-bottom: 2px solid #ccc;
cursor: pointer;
}
.k-table td {
border-bottom: 1px solid #eee;
}
.k-table th:focus,
.k-table td:focus {
outline: 2px solid #007bff;
outline-offset: -2px;
}
.sort-icon {
margin-left: 8px;
}
</style>
Loading

0 comments on commit 491fe19

Please sign in to comment.