mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-27 07:34:43 +02:00
Completes arrow-key navigation functionality
This commit is contained in:
parent
baff357854
commit
0a68333f6c
@ -17,98 +17,46 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import ArrowKeyNavigation from '@/utils/ArrowKeyNavigation';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FilterTile',
|
name: 'FilterTile',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
input: '',
|
input: '', // Users current search term
|
||||||
index: undefined,
|
akn: new ArrowKeyNavigation(), // Class that manages arrow key naviagtion
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
userIsTypingSomething() {
|
|
||||||
this.$emit('user-is-searchin', this.input);
|
|
||||||
},
|
|
||||||
clearFilterInput() {
|
|
||||||
this.input = '';
|
|
||||||
this.userIsTypingSomething();
|
|
||||||
document.activeElement.blur();
|
|
||||||
this.index = undefined;
|
|
||||||
},
|
|
||||||
/* Returns the number of visible items / results */
|
|
||||||
getNumResults() {
|
|
||||||
return document.getElementsByClassName('item').length;
|
|
||||||
},
|
|
||||||
/* Returns the index for an element, ensuring that it's within bounds */
|
|
||||||
getSafeElementIndex(index) {
|
|
||||||
const numResults = this.getNumResults();
|
|
||||||
if (index < 0) return numResults - 1;
|
|
||||||
else if (index >= numResults) return 0;
|
|
||||||
return index;
|
|
||||||
},
|
|
||||||
/* Selects a given element, by it's ID. If out of bounds, returns element 0 */
|
|
||||||
selectElementByIndex(index) {
|
|
||||||
return (index >= 0 && index <= this.getNumResults())
|
|
||||||
? document.getElementsByClassName('item')[index] : [document.getElementsByClassName('item')];
|
|
||||||
},
|
|
||||||
findNextRow(index) {
|
|
||||||
const isSameRow = (indx, pos) => this.selectElementByIndex(indx).offsetTop === pos;
|
|
||||||
const checkNextIndex = (currentIndex, yPos) => {
|
|
||||||
if (currentIndex >= this.getNumResults()) return checkNextIndex(0, yPos);
|
|
||||||
else if (isSameRow(currentIndex, yPos)) return checkNextIndex(currentIndex + 1, yPos);
|
|
||||||
return currentIndex;
|
|
||||||
};
|
|
||||||
const position = this.selectElementByIndex(index).offsetTop;
|
|
||||||
return checkNextIndex(index, position);
|
|
||||||
},
|
|
||||||
findPrevious(index) {
|
|
||||||
const isSameRow = (indx, pos) => this.selectElementByIndex(indx).offsetTop === pos;
|
|
||||||
const checkNextIndex = (currentIndex, yPos) => {
|
|
||||||
if (currentIndex >= this.getNumResults()) return checkNextIndex(0, yPos);
|
|
||||||
else if (isSameRow(currentIndex, yPos)) return checkNextIndex(currentIndex - 1, yPos);
|
|
||||||
return currentIndex;
|
|
||||||
};
|
|
||||||
const position = this.selectElementByIndex(index).offsetTop;
|
|
||||||
return checkNextIndex(index, position);
|
|
||||||
},
|
|
||||||
/* Figures out which element is next, based on the key pressed *
|
|
||||||
* current index and total number of items. Then calls focus function */
|
|
||||||
arrowNavigation(key, numResults) {
|
|
||||||
if (this.index === undefined) this.index = 0; // Start at beginning
|
|
||||||
else if (key === 37) { // Left --> Previous
|
|
||||||
this.index -= 1;
|
|
||||||
} else if (key === 38) { // Up --> Previous
|
|
||||||
this.index = this.findPrevious(this.index, numResults);
|
|
||||||
} else if (key === 39) { // Right --> Next
|
|
||||||
this.index += 1;
|
|
||||||
} else if (key === 40) { // Down --> Next
|
|
||||||
this.index = this.findNextRow(this.index, numResults);
|
|
||||||
}
|
|
||||||
/* Ensure the index is within bounds, then focus element */
|
|
||||||
this.index = this.getSafeElementIndex(this.index);
|
|
||||||
this.selectElementByIndex(this.index).focus();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
window.addEventListener('keydown', (event) => {
|
window.addEventListener('keydown', (event) => {
|
||||||
const currentElem = document.activeElement.id;
|
const currentElem = document.activeElement.id;
|
||||||
const { key, keyCode } = event;
|
const { key, keyCode } = event;
|
||||||
if (/^[a-zA-Z]$/.test(key) && currentElem !== 'filter-tiles') {
|
if (/^[a-zA-Z]$/.test(key) && currentElem !== 'filter-tiles') {
|
||||||
/* Letter key pressed - start searching */
|
/* Letter key pressed - start searching */
|
||||||
try {
|
|
||||||
this.$refs.filter.focus();
|
this.$refs.filter.focus();
|
||||||
this.userIsTypingSomething();
|
this.userIsTypingSomething();
|
||||||
} catch (e) { /* Do nothing */ }
|
|
||||||
} else if (keyCode >= 37 && keyCode <= 40) {
|
} else if (keyCode >= 37 && keyCode <= 40) {
|
||||||
/* Arrow key pressed - start navigation */
|
/* Arrow key pressed - start navigation */
|
||||||
const numResults = this.getNumResults();
|
this.akn.arrowNavigation(keyCode);
|
||||||
this.arrowNavigation(keyCode, numResults);
|
|
||||||
} else if (keyCode === 27) {
|
} else if (keyCode === 27) {
|
||||||
/* Esc key pressed - reset form */
|
/* Esc key pressed - reset form */
|
||||||
this.clearFilterInput();
|
this.clearFilterInput();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
/* Emmits users's search term up to parent */
|
||||||
|
userIsTypingSomething() {
|
||||||
|
this.$emit('user-is-searchin', this.input);
|
||||||
|
},
|
||||||
|
/* Resets everything to initial state, when user is finished */
|
||||||
|
clearFilterInput() {
|
||||||
|
this.input = ''; // Clear input model
|
||||||
|
this.userIsTypingSomething(); // Emmit new empty value
|
||||||
|
document.activeElement.blur(); // Remove focus
|
||||||
|
this.akn.resetIndex(); // Reset current element index
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
97
src/utils/ArrowKeyNavigation.js
Normal file
97
src/utils/ArrowKeyNavigation.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* Class encapsulating the functionality that enables the user to
|
||||||
|
* navigate through tiles/ search result grid using the arrow keys
|
||||||
|
* This code is very hacky, it's best not to look at it for too long
|
||||||
|
*/
|
||||||
|
export default class ArrowKeyNavigation {
|
||||||
|
constructor(index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetIndex() {
|
||||||
|
this.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figures out which element is next, based on the key pressed *
|
||||||
|
* current index and total number of items. Then calls focus function */
|
||||||
|
arrowNavigation(key) {
|
||||||
|
if (this.index === undefined) this.index = 0; // Start at beginning
|
||||||
|
else if (key === 37) { // Left --> Previous
|
||||||
|
this.index -= 1;
|
||||||
|
} else if (key === 38) { // Up --> Previous
|
||||||
|
this.index = ArrowKeyNavigation.goToPrevious(this.index);
|
||||||
|
} else if (key === 39) { // Right --> Next
|
||||||
|
this.index += 1;
|
||||||
|
} else if (key === 40) { // Down --> Next
|
||||||
|
this.index = ArrowKeyNavigation.goToNext(this.index);
|
||||||
|
}
|
||||||
|
/* Ensure the index is within bounds, then focus element */
|
||||||
|
this.index = ArrowKeyNavigation.getSafeElementIndex(this.index);
|
||||||
|
ArrowKeyNavigation.selectItemByIndex(this.index).focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of visible items / results */
|
||||||
|
static getNumResults() {
|
||||||
|
return document.getElementsByClassName('item').length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the index for an element, ensuring that it's within bounds */
|
||||||
|
static getSafeElementIndex(index) {
|
||||||
|
const numResults = ArrowKeyNavigation.getNumResults();
|
||||||
|
if (index < 0) return numResults - 1;
|
||||||
|
else if (index >= numResults) return 0;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selects a given element, by it's ID. If out of bounds, returns element 0 */
|
||||||
|
static selectItemByIndex(index) {
|
||||||
|
return (index >= 0 && index <= ArrowKeyNavigation.getNumResults())
|
||||||
|
? document.getElementsByClassName('item')[index] : [document.getElementsByClassName('item')];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the index of the first cell in the previous/ above row */
|
||||||
|
static findPreviousRow(startingIndex) {
|
||||||
|
const isSameRow = (indx, pos) => ArrowKeyNavigation.selectItemByIndex(indx).offsetTop === pos;
|
||||||
|
const checkPreviousIndex = (currentIndex, yPos) => {
|
||||||
|
if (currentIndex >= ArrowKeyNavigation.getNumResults()) return checkPreviousIndex(0, yPos);
|
||||||
|
else if (isSameRow(currentIndex, yPos)) return checkPreviousIndex(currentIndex - 1, yPos);
|
||||||
|
return currentIndex;
|
||||||
|
};
|
||||||
|
const position = ArrowKeyNavigation.selectItemByIndex(startingIndex).offsetTop;
|
||||||
|
return checkPreviousIndex(startingIndex, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Moves to the cell directly above the current */
|
||||||
|
static goToPrevious(startingIndex) {
|
||||||
|
const isBelow = (start, end) => (ArrowKeyNavigation.selectItemByIndex(start).offsetTop
|
||||||
|
< ArrowKeyNavigation.selectItemByIndex(end).offsetTop);
|
||||||
|
const nextIndex = ArrowKeyNavigation.findPreviousRow(startingIndex);
|
||||||
|
const count = nextIndex - startingIndex;
|
||||||
|
const rowLen = nextIndex - ArrowKeyNavigation.findNextRow(startingIndex) + 1;
|
||||||
|
const adjustment = isBelow(startingIndex, nextIndex) ? 0 : rowLen - count;
|
||||||
|
return nextIndex + adjustment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the index of the first cell in the next/ below row */
|
||||||
|
static findNextRow(startingIndex) {
|
||||||
|
const isSameRow = (indx, pos) => ArrowKeyNavigation.selectItemByIndex(indx).offsetTop === pos;
|
||||||
|
const checkNextIndex = (currentIndex, yPos) => {
|
||||||
|
if (currentIndex >= ArrowKeyNavigation.getNumResults()) return checkNextIndex(0, yPos);
|
||||||
|
else if (isSameRow(currentIndex, yPos)) return checkNextIndex(currentIndex + 1, yPos);
|
||||||
|
return currentIndex;
|
||||||
|
};
|
||||||
|
const position = ArrowKeyNavigation.selectItemByIndex(startingIndex).offsetTop;
|
||||||
|
return checkNextIndex(startingIndex, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Moves to the cell directly below the current */
|
||||||
|
static goToNext(startingIndex) {
|
||||||
|
const isAbove = (start, end) => (ArrowKeyNavigation.selectItemByIndex(start).offsetTop
|
||||||
|
> ArrowKeyNavigation.selectItemByIndex(end).offsetTop);
|
||||||
|
const nextIndex = ArrowKeyNavigation.findNextRow(startingIndex);
|
||||||
|
const count = nextIndex - startingIndex;
|
||||||
|
const rowLen = nextIndex - ArrowKeyNavigation.findPreviousRow(startingIndex) - 1;
|
||||||
|
const adjustment = isAbove(startingIndex, nextIndex) ? 0 : rowLen - count;
|
||||||
|
return nextIndex + adjustment;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user