From c8eb54ab98ceaf75f321f65b4a1ed314fe66f27b Mon Sep 17 00:00:00 2001 From: KoloMl Date: Fri, 5 Jul 2024 01:06:11 +0400 Subject: [PATCH] Removing auto-completion logic --- manifest.json | 11 -- src/content/header.js | 7 - src/lib/components/SearchWrapper.js | 186 ------------------------ src/lib/components/SiteHeaderWrapper.js | 23 --- src/styles/content/header.scss | 11 -- 5 files changed, 238 deletions(-) delete mode 100644 src/content/header.js delete mode 100644 src/lib/components/SearchWrapper.js delete mode 100644 src/lib/components/SiteHeaderWrapper.js delete mode 100644 src/styles/content/header.scss diff --git a/manifest.json b/manifest.json index 01fbe9e..9781982 100644 --- a/manifest.json +++ b/manifest.json @@ -26,17 +26,6 @@ "css": [ "src/styles/content/listing.scss" ] - }, - { - "matches": [ - "*://*.furbooru.org/*" - ], - "js": [ - "src/content/header.js" - ], - "css": [ - "src/styles/content/header.scss" - ] } ], "action": { diff --git a/src/content/header.js b/src/content/header.js deleted file mode 100644 index b9f3cec..0000000 --- a/src/content/header.js +++ /dev/null @@ -1,7 +0,0 @@ -import {initializeSiteHeader} from "$lib/components/SiteHeaderWrapper.js"; - -const siteHeader = document.querySelector('.header'); - -if (siteHeader) { - initializeSiteHeader(siteHeader); -} diff --git a/src/lib/components/SearchWrapper.js b/src/lib/components/SearchWrapper.js deleted file mode 100644 index d845589..0000000 --- a/src/lib/components/SearchWrapper.js +++ /dev/null @@ -1,186 +0,0 @@ -import {BaseComponent} from "$lib/components/base/BaseComponent.js"; -import {QueryLexer, QuotedTermToken, TermToken, Token} from "$lib/booru/search/QueryLexer.js"; - -export class SearchWrapper extends BaseComponent { - /** @type {HTMLInputElement|null} */ - #searchField = null; - /** @type {HTMLInputElement|null} */ - #autoCompleteField = null; - /** @type {string|null} */ - #lastParsedSearchValue = null; - /** @type {Token[]} */ - #cachedParsedQuery = []; - - build() { - this.container.classList.add('header__search--completable'); - - this.#searchField = this.container.querySelector('input[name=q]'); - this.#searchField.autocomplete = 'off'; // Browser's auto-complete will get in the way! - - const autoCompleteField = document.createElement('input'); - autoCompleteField.dataset.ac = 'true'; - autoCompleteField.dataset.acMinLength = '3'; - autoCompleteField.dataset.acSource = '/autocomplete/tags?term='; - autoCompleteField.classList.add('search-autocomplete-dummy'); - - this.#autoCompleteField = autoCompleteField; - - this.container.appendChild(autoCompleteField); - } - - init() { - this.#searchField.addEventListener('input', this.#updateAutoCompletedFragment.bind(this)); - this.#searchField.addEventListener('keydown', this.#onSearchFieldKeyPressed.bind(this)); - this.#searchField.addEventListener('selectionchange', this.#updateAutoCompletedFragment.bind(this)); - } - - #updateAutoCompletedFragment() { - const searchableFragment = this.#findCurrentTagFragment(); - this.#emitAutoComplete(searchableFragment || ''); - } - - #getInputUserSelection() { - return Math.min( - this.#searchField.selectionStart, - this.#searchField.selectionEnd - ); - } - - #resolveQueryTokens() { - const searchValue = this.#searchField.value; - - if (searchValue === this.#lastParsedSearchValue && this.#cachedParsedQuery) { - return this.#cachedParsedQuery; - } - - this.#lastParsedSearchValue = searchValue; - this.#cachedParsedQuery = new QueryLexer(searchValue).parse(); - - return this.#cachedParsedQuery; - } - - /** - * @param {KeyboardEvent} event - */ - #onSearchFieldKeyPressed(event) { - // On enter, attempt to replace the current active tag in the query with autocomplete selection - if (event.code === 'Enter') { - this.#onEnterPressed(event); - } - - this.#autoCompleteField.dispatchEvent( - new KeyboardEvent('keydown', { - keyCode: event.keyCode - }) - ); - - // Similarly to the site's autocomplete logic, we need to prevent the arrows up/down from causing any issues - if (event.keyCode === 38 || event.keyCode === 40) { - event.preventDefault(); - } - } - - /** - * @param {KeyboardEvent} event - */ - #onEnterPressed(event) { - const autocompleteSelection = document.querySelector('.autocomplete__item--selected'); - - if (!autocompleteSelection) { - return; - } - - const activeToken = SearchWrapper.#findActiveSearchTermPosition( - this.#resolveQueryTokens(), - this.#getInputUserSelection(), - ); - - if (activeToken instanceof TermToken || activeToken instanceof QuotedTermToken) { - const selectionStart = activeToken.index; - const selectionEnd = activeToken.index + activeToken.value.length; - - let autocompletedValue = autocompleteSelection.dataset.value; - - if (activeToken instanceof QuotedTermToken) { - autocompletedValue = `"${QuotedTermToken.encode(autocompletedValue)}"`; - } - - this.#searchField.value = this.#searchField.value.slice(0, selectionStart) - + autocompletedValue - + this.#searchField.value.slice(selectionEnd); - - const newSelectionEnd = selectionStart + autocompletedValue.length; - - // Place the caret at the end of the currently active tag. - // Actually, this does not work for some reason. After the tag is sent to the field and selection was changed to - // the end of the inserted tag, browser just does not scroll the input to the caret position. - this.#searchField.focus(); - this.#searchField.setSelectionRange(newSelectionEnd, newSelectionEnd); - - event.preventDefault(); - } - } - - /** - * @return {string|null} - */ - #findCurrentTagFragment() { - if (!this.#searchField) { - return null; - } - - let searchValue = this.#searchField.value; - - if (!searchValue) { - return null; - } - - const token = SearchWrapper.#findActiveSearchTermPosition( - this.#resolveQueryTokens(), - this.#getInputUserSelection(), - ); - - if (token instanceof TermToken) { - return token.value; - } - - if (token instanceof QuotedTermToken) { - return token.decodedValue; - } - - return searchValue; - } - - #emitAutoComplete(userInputFragment) { - this.#autoCompleteField.value = userInputFragment; - - // Should be at least one frame away, since input event always removes autocomplete window - requestAnimationFrame(() => { - this.#autoCompleteField.dispatchEvent( - new InputEvent('input', {bubbles: true}) - ); - - const autocompleteContainer = document.querySelector('.autocomplete'); - - if (autocompleteContainer) { - autocompleteContainer.style.left = `${this.container.offsetLeft}px`; - } - }); - } - - /** - * Loosely estimate where current selected search term is located and return it if found. - * @param {Token[]} tokens Search value to find the actively selected term from. - * @param {number} userSelectionIndex The index of the user selection. - * @return {Token|null} Search term object or NULL if nothing found. - */ - static #findActiveSearchTermPosition(tokens, userSelectionIndex) { - return tokens.find( - token => token.index < userSelectionIndex && token.index + token.value.length >= userSelectionIndex - ); - } -} - -export function initializeSearWrapper(formElement) { - new SearchWrapper(formElement).initialize(); -} diff --git a/src/lib/components/SiteHeaderWrapper.js b/src/lib/components/SiteHeaderWrapper.js deleted file mode 100644 index f8a1160..0000000 --- a/src/lib/components/SiteHeaderWrapper.js +++ /dev/null @@ -1,23 +0,0 @@ -import {BaseComponent} from "$lib/components/base/BaseComponent.js"; -import {SearchWrapper} from "$lib/components/SearchWrapper.js"; - -class SiteHeaderWrapper extends BaseComponent { - /** @type {SearchWrapper|null} */ - #searchWrapper = null; - - build() { - const searchForm = this.container.querySelector('.header__search'); - this.#searchWrapper = searchForm && new SearchWrapper(searchForm) || null; - } - - init() { - if (this.#searchWrapper) { - this.#searchWrapper.initialize(); - } - } -} - -export function initializeSiteHeader(siteHeaderElement) { - new SiteHeaderWrapper(siteHeaderElement) - .initialize(); -} diff --git a/src/styles/content/header.scss b/src/styles/content/header.scss deleted file mode 100644 index 81ea625..0000000 --- a/src/styles/content/header.scss +++ /dev/null @@ -1,11 +0,0 @@ -.header__search--completable { - .search-autocomplete-dummy { - position: absolute; - height: 0; - opacity: 0; - pointer-events: none; - padding: 0; - margin: 0; - align-self: flex-end; - } -}