Refactor filtered search manager

This commit is contained in:
Clement Ho 2016-12-12 09:21:38 -06:00
parent f4db75728e
commit 513cdda316
6 changed files with 185 additions and 157 deletions

View File

@ -42,7 +42,7 @@
const tag = selected.querySelector('.js-filter-tag').innerText.trim();
if (tag.length) {
gl.FilteredSearchManager.addWordToInput(this.getSelectedText(token));
gl.FilteredSearchDropdownManager.addWordToInput(this.getSelectedText(token));
}
this.dismissDropdown();
this.dispatchInputEvent();

View File

@ -25,7 +25,7 @@
if (!dataValueSet) {
const title = e.detail.selected.querySelector('.js-data-value').innerText.trim();
const name = `${this.symbol}${this.getEscapedText(title)}`;
gl.FilteredSearchManager.addWordToInput(this.getSelectedText(name));
gl.FilteredSearchDropdownManager.addWordToInput(this.getSelectedText(name));
}
this.dismissDropdown(!dataValueSet);

View File

@ -27,7 +27,7 @@
if (!dataValueSet) {
const username = e.detail.selected.querySelector('.dropdown-light-content').innerText.trim();
gl.FilteredSearchManager.addWordToInput(this.getSelectedText(username));
gl.FilteredSearchDropdownManager.addWordToInput(this.getSelectedText(username));
}
this.dismissDropdown(!dataValueSet);

View File

@ -57,7 +57,7 @@
const dataValue = selected.getAttribute('data-value');
if (dataValue) {
gl.FilteredSearchManager.addWordToInput(dataValue);
gl.FilteredSearchDropdownManager.addWordToInput(dataValue);
}
return dataValue !== null;

View File

@ -0,0 +1,174 @@
/* eslint-disable no-param-reassign */
((global) => {
class FilteredSearchDropdownManager {
constructor() {
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchInput = document.querySelector('.filtered-search');
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('page:fetch', this.cleanupWrapper);
}
cleanup() {
if (this.droplab) {
this.droplab.destroy();
this.droplab = null;
}
this.setupMapping();
document.removeEventListener('page:fetch', this.cleanupWrapper);
}
setupMapping() {
this.mapping = {
author: {
reference: null,
gl: 'DropdownUser',
element: document.querySelector('#js-dropdown-author'),
},
assignee: {
reference: null,
gl: 'DropdownUser',
element: document.querySelector('#js-dropdown-assignee'),
},
milestone: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: ['milestones.json', '%'],
element: document.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: ['labels.json', '~'],
element: document.querySelector('#js-dropdown-label'),
},
hint: {
reference: null,
gl: 'DropdownHint',
element: document.querySelector('#js-dropdown-hint'),
},
}
}
static addWordToInput(word, addSpace) {
const filteredSearchInput = document.querySelector('.filtered-search')
const filteredSearchValue = filteredSearchInput.value;
const hasExistingValue = filteredSearchValue.length !== 0;
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(filteredSearchValue);
if (lastToken.hasOwnProperty('key')) {
console.log(lastToken);
// Spaces inside the token means that the token value will be escaped by quotes
const hasQuotes = lastToken.value.indexOf(' ') !== -1;
const lengthToRemove = hasQuotes ? lastToken.value.length + 2 : lastToken.value.length;
filteredSearchInput.value = filteredSearchValue.slice(0, -1 * (lengthToRemove));
}
filteredSearchInput.value += hasExistingValue && addSpace ? ` ${word}` : word;
}
updateCurrentDropdownOffset() {
this.updateDropdownOffset(this.currentDropdown);
}
updateDropdownOffset(key) {
const filterIconPadding = 27;
const offset = gl.text.getTextWidth(this.filteredSearchInput.value, this.font) + filterIconPadding;
this.mapping[key].reference.setOffset(offset);
}
load(key, firstLoad = false) {
console.log(`🦄 load ${key} dropdown`);
const glClass = this.mapping[key].gl;
const element = this.mapping[key].element;
let forceShowList = false;
if (!this.mapping[key].reference) {
var dl = this.droplab;
const defaultArguments = [null, dl, element, this.filteredSearchInput];
const glArguments = defaultArguments.concat(this.mapping[key].extraArguments || []);
this.mapping[key].reference = new (Function.prototype.bind.apply(gl[glClass], glArguments));
}
if (firstLoad) {
this.mapping[key].reference.configure();
}
if (this.currentDropdown === 'hint') {
// Clicked from hint dropdown
forceShowList = true;
}
this.updateDropdownOffset(key);
this.mapping[key].reference.render(firstLoad, forceShowList);
this.currentDropdown = key;
}
loadDropdown(dropdownName = '') {
let firstLoad = false;
if(!this.droplab) {
firstLoad = true;
this.droplab = new DropLab();
}
if (!this.font) {
this.font = window.getComputedStyle(this.filteredSearchInput).font;
}
const match = gl.FilteredSearchTokenKeys.get().filter(value => value.key === dropdownName.toLowerCase())[0];
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key && this.mapping.hasOwnProperty(match.key);
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
const key = match && match.hasOwnProperty('key') ? match.key : 'hint';
this.load(key, firstLoad);
}
gl.droplab = this.droplab;
}
setDropdown() {
const { lastToken } = this.tokenizer.processTokens(this.filteredSearchInput.value);
if (typeof lastToken === 'string') {
// Token is not fully initialized yet
// because it has no value
// Eg. token = 'label:'
const { tokenKey } = this.tokenizer.parseToken(lastToken);
this.loadDropdown(tokenKey);
} else if (lastToken.hasOwnProperty('key')) {
// Token has been initialized into an object
// because it has a value
this.loadDropdown(lastToken.key);
} else {
this.loadDropdown('hint');
}
}
resetDropdowns() {
// Force current dropdown to hide
this.mapping[this.currentDropdown].reference.hideDropdown();
// Re-Load dropdown
this.setDropdown();
// Reset filters for current dropdown
this.mapping[this.currentDropdown].reference.resetFilters();
// Reposition dropdown so that it is aligned with cursor
this.updateDropdownOffset(this.currentDropdown);
}
destroyDroplab() {
this.droplab.destroy();
}
}
global.FilteredSearchDropdownManager = FilteredSearchDropdownManager;
})(window.gl || (window.gl = {}));

View File

@ -75,159 +75,24 @@
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchInput = document.querySelector('.filtered-search');
this.clearSearchButton = document.querySelector('.clear-search');
this.dropdownManager = new gl.FilteredSearchDropdownManager();
this.setupMapping();
this.dropdownManager.setupMapping();
this.bindEvents();
loadSearchParamsFromURL();
this.setDropdown();
this.dropdownManager.setDropdown();
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('page:fetch', this.cleanupWrapper);
}
cleanup() {
console.log('cleanup')
if (this.droplab) {
this.droplab.destroy();
this.droplab = null;
}
this.setupMapping();
this.unbindEvents();
document.removeEventListener('page:fetch', this.cleanupWrapper);
}
setupMapping() {
this.mapping = {
author: {
reference: null,
gl: 'DropdownUser',
element: document.querySelector('#js-dropdown-author'),
},
assignee: {
reference: null,
gl: 'DropdownUser',
element: document.querySelector('#js-dropdown-assignee'),
},
milestone: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: ['milestones.json', '%'],
element: document.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: ['labels.json', '~'],
element: document.querySelector('#js-dropdown-label'),
},
hint: {
reference: null,
gl: 'DropdownHint',
element: document.querySelector('#js-dropdown-hint'),
},
}
}
static addWordToInput(word, addSpace) {
const filteredSearchInput = document.querySelector('.filtered-search')
const filteredSearchValue = filteredSearchInput.value;
const hasExistingValue = filteredSearchValue.length !== 0;
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(filteredSearchValue);
if (lastToken.hasOwnProperty('key')) {
console.log(lastToken);
// Spaces inside the token means that the token value will be escaped by quotes
const hasQuotes = lastToken.value.indexOf(' ') !== -1;
const lengthToRemove = hasQuotes ? lastToken.value.length + 2 : lastToken.value.length;
filteredSearchInput.value = filteredSearchValue.slice(0, -1 * (lengthToRemove));
}
filteredSearchInput.value += hasExistingValue && addSpace ? ` ${word}` : word;
}
updateDropdownOffset(key) {
const filterIconPadding = 27;
const offset = gl.text.getTextWidth(this.filteredSearchInput.value, this.font) + filterIconPadding;
this.mapping[key].reference.setOffset(offset);
}
load(key, firstLoad = false) {
console.log(`🦄 load ${key} dropdown`);
const glClass = this.mapping[key].gl;
const element = this.mapping[key].element;
let forceShowList = false;
if (!this.mapping[key].reference) {
var dl = this.droplab;
const defaultArguments = [null, dl, element, this.filteredSearchInput];
const glArguments = defaultArguments.concat(this.mapping[key].extraArguments || []);
this.mapping[key].reference = new (Function.prototype.bind.apply(gl[glClass], glArguments));
}
if (firstLoad) {
this.mapping[key].reference.configure();
}
if (this.currentDropdown === 'hint') {
// Clicked from hint dropdown
forceShowList = true;
}
this.updateDropdownOffset(key);
this.mapping[key].reference.render(firstLoad, forceShowList);
this.currentDropdown = key;
}
loadDropdown(dropdownName = '') {
let firstLoad = false;
if(!this.droplab) {
firstLoad = true;
this.droplab = new DropLab();
}
if (!this.font) {
this.font = window.getComputedStyle(this.filteredSearchInput).font;
}
const match = gl.FilteredSearchTokenKeys.get().filter(value => value.key === dropdownName.toLowerCase())[0];
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key && this.mapping.hasOwnProperty(match.key);
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
const key = match && match.hasOwnProperty('key') ? match.key : 'hint';
this.load(key, firstLoad);
}
gl.droplab = this.droplab;
}
setDropdown() {
const { lastToken } = this.tokenizer.processTokens(this.filteredSearchInput.value);
if (typeof lastToken === 'string') {
// Token is not fully initialized yet
// because it has no value
// Eg. token = 'label:'
const { tokenKey } = this.tokenizer.parseToken(lastToken);
this.loadDropdown(tokenKey);
} else if (lastToken.hasOwnProperty('key')) {
// Token has been initialized into an object
// because it has a value
this.loadDropdown(lastToken.key);
} else {
this.loadDropdown('hint');
}
}
bindEvents() {
this.setDropdownWrapper = this.setDropdown.bind(this);
this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
this.checkForEnterWrapper = this.checkForEnter.bind(this);
this.clearSearchWrapper = this.clearSearch.bind(this);
this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);
@ -254,24 +119,13 @@
this.filteredSearchInput.value = '';
this.clearSearchButton.classList.add('hidden');
// Force current dropdown to hide
this.mapping[this.currentDropdown].reference.hideDropdown();
// Re-Load dropdown
this.setDropdown();
// Reset filters for current dropdown
this.mapping[this.currentDropdown].reference.resetFilters();
// Reposition dropdown so that it is aligned with cursor
this.updateDropdownOffset(this.currentDropdown);
this.dropdownManager.resetDropdowns();
}
checkForBackspace(e) {
if (e.keyCode === 8) {
// Reposition dropdown so that it is aligned with cursor
this.updateDropdownOffset(this.currentDropdown);
this.dropdownManager.updateCurrentDropdownOffset();
}
}
@ -282,7 +136,7 @@
e.preventDefault();
// Prevent droplab from opening dropdown
this.droplab.destroy();
this.dropdownManager.destroyDroplab();
this.search();
}