From 00b4aec60a804104b9d89d03152922e2ea8d05c9 Mon Sep 17 00:00:00 2001 From: REJack Date: Sat, 13 Jun 2020 17:32:43 +0200 Subject: [PATCH] add SidebarSearch.js --- build/js/AdminLTE.js | 2 + build/js/SidebarSearch.js | 225 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 build/js/SidebarSearch.js diff --git a/build/js/AdminLTE.js b/build/js/AdminLTE.js index cb004e543..fd709a432 100644 --- a/build/js/AdminLTE.js +++ b/build/js/AdminLTE.js @@ -6,6 +6,7 @@ import Dropdown from './Dropdown' import ExpandableTable from './ExpandableTable' import Layout from './Layout' import PushMenu from './PushMenu' +import SidebarSearch from './SidebarSearch' import Toasts from './Toasts' import TodoList from './TodoList' import Treeview from './Treeview' @@ -19,6 +20,7 @@ export { ExpandableTable, Layout, PushMenu, + SidebarSearch, Toasts, TodoList, Treeview diff --git a/build/js/SidebarSearch.js b/build/js/SidebarSearch.js new file mode 100644 index 000000000..0eacbba95 --- /dev/null +++ b/build/js/SidebarSearch.js @@ -0,0 +1,225 @@ +/** + * -------------------------------------------- + * AdminLTE SidebarSearch.js + * License MIT + * -------------------------------------------- + */ + +import $, { trim } from 'jquery' + +/** + * Constants + * ==================================================== + */ + +const NAME = 'SidebarSearch' +const DATA_KEY = 'lte.sidebar-search' +const JQUERY_NO_CONFLICT = $.fn[NAME] + +const CLASS_NAME_OPEN = 'sidebar-search-open' +const CLASS_NAME_ICON_SEARCH = 'fa-search' +const CLASS_NAME_ICON_CLOSE = 'fa-times' +const CLASS_NAME_HEADER = 'nav-header' +const CLASS_NAME_SEARCH_RESULTS = 'sidebar-search-results' +const CLASS_NAME_LIST_GROUP = 'list-group' + +const SELECTOR_DATA_WIDGET = '[data-widget="sidebar-search"]' +const SELECTOR_SIDEBAR = '.main-sidebar .nav-sidebar' +const SELECTOR_NAV_LINK = '.nav-link' +const SELECTOR_NAV_TREEVIEW = '.nav-treeview' +const SELECTOR_SEARCH_INPUT = `${SELECTOR_DATA_WIDGET} .form-control` +const SELECTOR_SEARCH_BUTTON = `${SELECTOR_DATA_WIDGET} .btn` +const SELECTOR_SEARCH_ICON = `${SELECTOR_SEARCH_BUTTON} i` +const SELECTOR_SEARCH_LIST_GROUP = `.${CLASS_NAME_LIST_GROUP}` +const SELECTOR_SEARCH_RESULTS = `.${CLASS_NAME_SEARCH_RESULTS}` +const SELECTOR_SEARCH_RESULTS_GROUP = `${SELECTOR_SEARCH_RESULTS} .${CLASS_NAME_LIST_GROUP}` + +const Default = { + arrowSign: '->', + minLength: 3, + maxResults: 7, + notFoundText: 'No element found!' +} + +const SearchItems = [] + +/** + * Class Definition + * ==================================================== + */ + +class SidebarSearch { + constructor(_element, _options) { + this.element = _element + this.options = $.extend({}, Default, _options) + this.items = [] + } + + // Public + + init() { + if ($(SELECTOR_DATA_WIDGET).next(SELECTOR_SEARCH_RESULTS).length == 0) { + $(SELECTOR_DATA_WIDGET).after( + $('
', { class: CLASS_NAME_SEARCH_RESULTS }) + ) + } + + if ($(SELECTOR_SEARCH_RESULTS).children(SELECTOR_SEARCH_LIST_GROUP).length == 0) { + $(SELECTOR_SEARCH_RESULTS).append( + $('
', { class: CLASS_NAME_LIST_GROUP }) + ) + } + + this._addNotFound() + + $(SELECTOR_SIDEBAR).children().each((i, child) => { + this._parseItem(child) + }) + } + + search() { + const searchValue = $(SELECTOR_SEARCH_INPUT).val().toLowerCase() + if (searchValue.length < this.options.minLength) { + $(SELECTOR_SEARCH_RESULTS_GROUP).empty() + this._addNotFound() + this.close() + return + } + + const searchResults = SearchItems.filter(item => (item.name).toLowerCase().includes(searchValue)) + const endResults = $(searchResults.slice(0, this.options.maxResults)) + $(SELECTOR_SEARCH_RESULTS_GROUP).empty() + + if (endResults.length === 0) { + this._addNotFound() + } else { + endResults.each((i, result) => { + $(SELECTOR_SEARCH_RESULTS_GROUP).append(this._renderItem(result.name, result.link, result.path)) + }) + } + + this.open() + } + + open() { + $(SELECTOR_DATA_WIDGET).parent().addClass(CLASS_NAME_OPEN) + $(SELECTOR_SEARCH_ICON).removeClass(CLASS_NAME_ICON_SEARCH).addClass(CLASS_NAME_ICON_CLOSE) + } + + close() { + $(SELECTOR_DATA_WIDGET).parent().removeClass(CLASS_NAME_OPEN) + $(SELECTOR_SEARCH_ICON).removeClass(CLASS_NAME_ICON_CLOSE).addClass(CLASS_NAME_ICON_SEARCH) + } + + toggle() { + if ($(SELECTOR_DATA_WIDGET).parent().hasClass(CLASS_NAME_OPEN)) { + this.close() + } else { + this.open() + } + } + + // Private + + _parseItem(item, path = []) { + if ($(item).hasClass(CLASS_NAME_HEADER)) { + return + } + + const itemObject = {} + const navLink = $(item).clone().find(`> ${SELECTOR_NAV_LINK}`) + const navTreeview = $(item).clone().find(`> ${SELECTOR_NAV_TREEVIEW}`) + + const link = navLink.attr('href') + const name = navLink.find('p').children().remove().end().text() + + itemObject.name = this._trimText(name) + itemObject.link = link + itemObject.path = path + + if (navTreeview.length === 0) { + SearchItems.push(itemObject) + } else { + const newPath = itemObject.path.concat([itemObject.name]) + navTreeview.children().each((i, child) => { + this._parseItem(child, newPath) + }) + } + } + + _trimText(text) { + return trim(text.replace(/(\r\n|\n|\r)/gm, ' ')) + } + + _renderItem(name, link, path) { + return ` +
+ ${name} +
+
+ ${path.join(` ${this.options.arrowSign} `)} +
+
` + } + + _addNotFound() { + $(SELECTOR_SEARCH_RESULTS_GROUP).append(this._renderItem(this.options.notFoundText, '#', [])) + } + + // Static + + static _jQueryInterface(config) { + let data = $(this).data(DATA_KEY) + + if (!data) { + data = $(this).data() + } + + const _options = $.extend({}, Default, typeof config === 'object' ? config : data) + const plugin = new SidebarSearch($(this), _options) + + $(this).data(DATA_KEY, typeof config === 'object' ? config : data) + + if (typeof config === 'string' && config.match(/init|toggle|close|open|search/)) { + plugin[config]() + } else { + plugin.init() + } + } +} + +/** + * Data API + * ==================================================== + */ +$(document).on('click', SELECTOR_SEARCH_BUTTON, event => { + event.preventDefault() + + SidebarSearch._jQueryInterface.call($(SELECTOR_DATA_WIDGET), 'toggle') +}) + +$(document).on('keyup', SELECTOR_SEARCH_INPUT, () => { + let timer = 0 + clearTimeout(timer) + timer = setTimeout(() => { + SidebarSearch._jQueryInterface.call($(SELECTOR_DATA_WIDGET), 'search') + }, 100) +}) + +$(window).on('load', () => { + SidebarSearch._jQueryInterface.call($(SELECTOR_DATA_WIDGET), 'init') +}) + +/** + * jQuery API + * ==================================================== + */ + +$.fn[NAME] = SidebarSearch._jQueryInterface +$.fn[NAME].Constructor = SidebarSearch +$.fn[NAME].noConflict = function () { + $.fn[NAME] = JQUERY_NO_CONFLICT + return SidebarSearch._jQueryInterface +} + +export default SidebarSearch