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 ` +